Skip to content

Commit

Permalink
chore: extract examples as testable units
Browse files Browse the repository at this point in the history
- Added a new script `embed_examples.gleam` to embed code examples in the README.md file.

Attribution: The `embed_examples.gleam` file was written with kind help from Anthropic's Claude 3.5 Sonnet.
Signed-off-by: Aleksei Gurianov <[email protected]>
  • Loading branch information
Guria committed Oct 28, 2024
1 parent 0b6190d commit 534abf7
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 30 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ jobs:
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test
- run: gleam run -m scripts/embed_examples -- --check README.md
1 change: 1 addition & 0 deletions .hooks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
setup_env
4 changes: 4 additions & 0 deletions .hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
[ -f ./.hooks/setup_env ] && source ./.hooks/setup_env
gleam run -m scripts/embed_examples -- README.md
git add README.md
3 changes: 3 additions & 0 deletions .hooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env sh
[ -f ./.hooks/setup_env ] && source ./.hooks/setup_env
gleam run -m scripts/embed_examples -- --check README.md
52 changes: 38 additions & 14 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,45 @@ Thank you for your interest in contributing to GleoJSON! We welcome contribution
## Getting Started

1. Fork the repository on GitHub.
2. Clone your fork locally:
1. Clone your fork locally:
```
git clone https://github.com/guria/gleojson.git
cd gleojson
```
3. Set up your development environment with Gleam. If you haven't installed Gleam yet, follow the [official installation guide](https://gleam.run/getting-started/index.html).
1. Set up your development environment with Gleam. If you haven't installed Gleam yet, follow the [official installation guide](https://gleam.run/getting-started/index.html).
1. Setup git hooks with `git config core.hooksPath .hooks` and create `.hooks/setup_env` script if your Gleam executable provided by a version manager like `asdf`.

## Making Changes

1. Create a new branch for your feature or bug fix:
```
git checkout -b your-feature-branch
```
2. Make your changes in the relevant files under the `src/` directory.
3. Add or update tests as necessary in the `test/` directory.
4. Run the tests to ensure your changes don't break existing functionality:
1. Make your changes in the relevant files under the `src/` directory.
1. Add or update tests as necessary in the `test/` directory.
1. Run the tests to ensure your changes don't break existing functionality:
```
gleam test
```
5. Update the documentation if your changes affect the public API or user-facing features.
1. Update the documentation if your changes affect the public API or user-facing features.
1. Run `gleam run -m scripts/embed_examples -- README.md` to update usage example embed into README.md.

## Submitting Changes

1. Commit your changes with a clear and descriptive commit message:

```
git commit -am "Add a brief description of your changes"
git commit -am -s "Add a brief description of your changes"
```
2. Push your branch to your fork on GitHub:

By using the `-s` option, you are signing off your commit, which certifies that you accept the Developer Certificate of Origin (DCO) as defined in this document.

1. Push your branch to your fork on GitHub:
```
git push origin your-feature-branch
```
3. Open a pull request against the main repository's `main` branch.
4. In your pull request description, explain the changes you've made and why they're necessary.
1. Open a pull request against the main repository's `main` branch.
1. In your pull request description, explain the changes you've made and why they're necessary.

## Code Style and Standards

Expand All @@ -49,7 +55,7 @@ Thank you for your interest in contributing to GleoJSON! We welcome contribution
If you find a bug or have a suggestion for improvement:

1. Check the [GitHub Issues](https://github.com/guria/gleojson/issues) to see if it has already been reported.
2. If not, open a new issue, providing as much detail as possible about the problem or suggestion.
1. If not, open a new issue, providing as much detail as possible about the problem or suggestion.

## Community and Communication

Expand All @@ -66,6 +72,24 @@ Thank you for contributing to GleoJSON!

By making a contribution to this project, I certify that:

1. The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or

2. The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or

(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or

(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.

(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
25 changes: 10 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,24 @@ gleam add gleojson

Here's a basic example of how to use gleojson:

```gleam
import gleojson
```gleam:./test/examples/encode.gleam
import gleam/json
import gleam/option
import gleam/io
import gleojson
pub fn main() {
// Create a Point geometry
let point = gleojson.Point(gleojson.new_position_2d(lon: 125.6, lat: 10.1))
gleojson.Point(gleojson.new_position_2d(lon: 125.6, lat: 10.1))
|> option.Some
// Create a Feature with the Point geometry
let feature = gleojson.Feature(
geometry: option.Some(point),
|> gleojson.Feature(
properties: option.None,
id: option.Some(gleojson.StringId("example-point"))
id: option.Some(gleojson.StringId("example-point")),
)
// Encode the Feature to GeoJSON
let geojson = gleojson.GeoFeature(feature)
let encoded = gleojson.encode_geojson(geojson, gleojson.properties_null_encoder)
// Print the encoded GeoJSON
io.println(json.to_string(encoded))
|> gleojson.GeoFeature
|> gleojson.encode_geojson(gleojson.properties_null_encoder)
|> json.to_string
}
```

Expand All @@ -89,4 +84,4 @@ Contributions to gleojson are welcome! Please feel free to submit a Pull Request

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

Please see the [NOTICE](NOTICE) file for information about third party components and the use of AI assistance in this project.
Please see the [NOTICE](NOTICE) file for information about third party components and the use of AI assistance in this project.
6 changes: 5 additions & 1 deletion gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ version = "0.3.0"
description = "A Gleam library for encoding and decoding GeoJSON"
licences = ["MIT"]
repository = { type = "github", user = "guria", repo = "gleojson" }
links = [{ title = "RFC7946", href = "https://datatracker.ietf.org/doc/html/rfc7946" }]
links = [
{ title = "RFC7946", href = "https://datatracker.ietf.org/doc/html/rfc7946" },
]

[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
Expand All @@ -13,3 +15,5 @@ gleam_json = ">= 2.0.0 and < 3.0.0"
[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
birdie = ">= 1.2.3 and < 2.0.0"
argv = ">= 1.0.2 and < 2.0.0"
simplifile = ">= 2.2.0 and < 3.0.0"
2 changes: 2 additions & 0 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ packages = [
]

[requirements]
argv = { version = ">= 1.0.2 and < 2.0.0" }
birdie = { version = ">= 1.2.3 and < 2.0.0" }
gleam_json = { version = ">= 2.0.0 and < 3.0.0" }
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
simplifile = { version = ">= 2.2.0 and < 3.0.0" }
18 changes: 18 additions & 0 deletions test/examples/encode.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import gleam/json
import gleam/option
import gleojson

pub fn main() {
// Create a Point geometry
gleojson.Point(gleojson.new_position_2d(lon: 125.6, lat: 10.1))
|> option.Some
// Create a Feature with the Point geometry
|> gleojson.Feature(
properties: option.None,
id: option.Some(gleojson.StringId("example-point")),
)
// Encode the Feature to GeoJSON
|> gleojson.GeoFeature
|> gleojson.encode_geojson(gleojson.properties_null_encoder)
|> json.to_string
}
8 changes: 8 additions & 0 deletions test/gleojson_test.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import birdie
import examples/encode
import gleam/dynamic
import gleam/json
import gleam/option
Expand Down Expand Up @@ -335,3 +336,10 @@ pub fn real_life_featurecollection_test() {
"real_life_featurecollection",
)
}

pub fn example_test() {
encode.main()
|> dynamic.from()
|> dynamic.classify()
|> should.equal("String")
}
122 changes: 122 additions & 0 deletions test/scripts/embed_examples.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import argv
import gleam/io
import gleam/list
import gleam/string
import simplifile

pub fn main() {
process_args(argv.load())
}

fn process_args(args: argv.Argv) {
case args.arguments {
["--check", filename] -> process_file(filename, True)
[filename] -> process_file(filename, False)
_ -> print_usage()
}
}

fn print_usage() {
io.println("Usage: [--check] <filename>")
io.println(" --check Check only mode (don't modify the file)")
io.println(" <filename> The markdown file to process")
}

fn process_file(filename: String, check_only: Bool) {
case simplifile.read(filename) {
Ok(content) -> {
let processed = process_content(content)
case check_only {
True -> compare_content(content, processed)
False -> update_file(filename, content, processed)
}
}
Error(error) -> io.println("Error reading file: " <> string.inspect(error))
}
}

type CodeBlockState {
NotInBlock
InRegularBlock
InAnnotatedBlock(filename: String)
}

type ProcessState {
ProcessState(output: String, code_block_state: CodeBlockState, buffer: String)
}

fn process_content(content: String) -> String {
content
|> string.split("\n")
|> list.fold(ProcessState("", NotInBlock, ""), fn(state, line) {
process_line(state, line)
})
|> fn(state) { state.output <> state.buffer }
|> string.trim
}

fn process_line(state: ProcessState, line: String) -> ProcessState {
case state.code_block_state, line {
NotInBlock, "```" <> rest -> {
case string.split(rest, ":") {
[lang, filepath] -> {
let trimmed_filepath = string.trim(filepath)
ProcessState(
state.output <> "```" <> lang <> ":" <> filepath <> "\n",
InAnnotatedBlock(trimmed_filepath),
"",
)
}
_ -> ProcessState(state.output <> line <> "\n", InRegularBlock, "")
}
}
InRegularBlock, "```" ->
ProcessState(state.output <> state.buffer <> "```\n", NotInBlock, "")
InRegularBlock, _ ->
ProcessState(state.output, InRegularBlock, state.buffer <> line <> "\n")
InAnnotatedBlock(filename), "```" -> {
case simplifile.read(filename) {
Ok(file_content) ->
ProcessState(
state.output <> string.trim(file_content) <> "\n```\n",
NotInBlock,
"",
)
Error(_) ->
ProcessState(
state.output <> "File not found: " <> filename <> "\n```\n",
NotInBlock,
"",
)
}
}
InAnnotatedBlock(_), _ -> state
NotInBlock, _ -> ProcessState(state.output <> line <> "\n", NotInBlock, "")
}
}

fn compare_content(original: String, processed: String) {
case original == processed {
True -> io.println("No changes detected.")
False -> {
io.println("Changes detected.")
exit(1)
}
}
}

fn update_file(filename: String, original: String, processed: String) {
case original == processed {
True -> io.println("No changes needed in '" <> filename <> "'.")
False -> {
case simplifile.write(filename, processed) {
Ok(_) -> io.println("Processed and updated '" <> filename <> "'.")
Error(error) ->
io.println("Error writing file: " <> string.inspect(error))
}
}
}
}

@external(erlang, "erlang", "halt")
fn exit(status: Int) -> Nil

0 comments on commit 534abf7

Please sign in to comment.