Compare commits
1 Commits
main
...
d5ed473e31
| Author | SHA1 | Date | |
|---|---|---|---|
| d5ed473e31 |
2
Makefile
2
Makefile
@@ -31,7 +31,7 @@ update-data:
|
|||||||
@command -v jq >/dev/null || { echo "update-data requires jq" >&2; exit 1; }
|
@command -v jq >/dev/null || { echo "update-data requires jq" >&2; exit 1; }
|
||||||
@url=$$(sed -n 's/^const BlogsURL = "\(.*\)"$$/\1/p' hnblogs.go); \
|
@url=$$(sed -n 's/^const BlogsURL = "\(.*\)"$$/\1/p' hnblogs.go); \
|
||||||
test -n "$$url" || { echo "could not parse BlogsURL from hnblogs.go" >&2; exit 1; }; \
|
test -n "$$url" || { echo "could not parse BlogsURL from hnblogs.go" >&2; exit 1; }; \
|
||||||
echo "downloading $$url"; \
|
echo "fetching $$url"; \
|
||||||
curl -fsSL "$$url" -o blogs.json.tmp
|
curl -fsSL "$$url" -o blogs.json.tmp
|
||||||
@jq -e 'type == "array" and length > 0 and all(.[]; type == "object" and has("url"))' \
|
@jq -e 'type == "array" and length > 0 and all(.[]; type == "object" and has("url"))' \
|
||||||
blogs.json.tmp >/dev/null 2>&1 \
|
blogs.json.tmp >/dev/null 2>&1 \
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -16,14 +16,25 @@ the package with `go:embed`. The library performs no network I/O: importing it
|
|||||||
does not reach out to anything, results do not change under a caller between
|
does not reach out to anything, results do not change under a caller between
|
||||||
runs of the same build, and `go test` works offline.
|
runs of the same build, and `go test` works offline.
|
||||||
|
|
||||||
`GetBlogs` decodes the embedded bytes on first call and memoizes the result;
|
`FetchBlogs` keeps its name and its `sync.Once` memoization, but on first call
|
||||||
every other accessor goes through it. Its error return is only reachable if the
|
it decodes the embedded bytes rather than issuing an HTTP request. Its error
|
||||||
committed `blogs.json` is malformed.
|
return is now only reachable if the committed `blogs.json` is malformed.
|
||||||
|
|
||||||
The trade-off is that the dataset is a build-time artifact: it is roughly 8 MB
|
The trade-off is that the dataset is a build-time artifact: it is roughly 8 MB
|
||||||
of JSON, it lands in every binary that links the package, and it is only as
|
of JSON, it lands in every binary that links the package, and it is only as
|
||||||
fresh as the last commit that refreshed it.
|
fresh as the last commit that refreshed it.
|
||||||
|
|
||||||
|
### Where blogs.json came from
|
||||||
|
|
||||||
|
`blogs.json` is not this project's work. It is an unmodified copy of
|
||||||
|
|
||||||
|
<https://raw.githubusercontent.com/surprisetalk/blogs.hn/main/blogs.json>
|
||||||
|
|
||||||
|
from the [surprisetalk/blogs.hn](https://github.com/surprisetalk/blogs.hn)
|
||||||
|
repository, which publishes no licence. This repository's `LICENSE` covers the
|
||||||
|
code here and does not extend to `blogs.json`, and a binary that links this
|
||||||
|
package redistributes that file too.
|
||||||
|
|
||||||
## Refreshing the dataset
|
## Refreshing the dataset
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
23
hnblogs.go
23
hnblogs.go
@@ -14,11 +14,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BlogsURL is the upstream source of blogs.json. Nothing reads it at runtime;
|
// BlogsURL is the upstream source of blogs.json. It is not fetched at runtime;
|
||||||
// it documents where "make update-data" downloads the vendored copy from.
|
// it documents where "make update-data" pulls the vendored copy from.
|
||||||
const BlogsURL = "https://raw.githubusercontent.com/surprisetalk/blogs.hn/main/blogs.json"
|
const BlogsURL = "https://raw.githubusercontent.com/surprisetalk/blogs.hn/main/blogs.json"
|
||||||
|
|
||||||
// blogsJSON is the vendored dataset, refreshed by "make update-data".
|
// blogsJSON is the vendored dataset, refreshed by "make update-data". It is an
|
||||||
|
// unmodified copy of a third party's file, not this project's work; see "Where
|
||||||
|
// blogs.json came from" in README.md.
|
||||||
//
|
//
|
||||||
//go:embed blogs.json
|
//go:embed blogs.json
|
||||||
var blogsJSON []byte
|
var blogsJSON []byte
|
||||||
@@ -39,13 +41,13 @@ type Blog struct {
|
|||||||
Desc string `json:"desc"`
|
Desc string `json:"desc"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlogs returns the embedded list of blogs, decoding it on first call and
|
// FetchBlogs returns the embedded list of blogs, decoding it on first call and
|
||||||
// memoizing the result for subsequent calls.
|
// memoizing the result for subsequent calls.
|
||||||
//
|
//
|
||||||
// It performs no I/O: the data is compiled into the binary, so the only error
|
// Despite the name it performs no I/O: the data is compiled into the binary, so
|
||||||
// it can return is a malformed embedded blogs.json, which would mean the
|
// the only error it can return is a malformed embedded blogs.json, which would
|
||||||
// committed dataset is broken.
|
// mean the committed dataset is broken.
|
||||||
func GetBlogs() ([]Blog, error) {
|
func FetchBlogs() ([]Blog, error) {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
var decoded []Blog
|
var decoded []Blog
|
||||||
if err := json.Unmarshal(blogsJSON, &decoded); err != nil {
|
if err := json.Unmarshal(blogsJSON, &decoded); err != nil {
|
||||||
@@ -59,6 +61,11 @@ func GetBlogs() ([]Blog, error) {
|
|||||||
return blogs, loadError
|
return blogs, loadError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBlogs returns the memoized list of blogs.
|
||||||
|
func GetBlogs() ([]Blog, error) {
|
||||||
|
return FetchBlogs()
|
||||||
|
}
|
||||||
|
|
||||||
// RandomBlog returns a random blog from the list of blogs.
|
// RandomBlog returns a random blog from the list of blogs.
|
||||||
func RandomBlog() (Blog, error) {
|
func RandomBlog() (Blog, error) {
|
||||||
blogs, err := GetBlogs()
|
blogs, err := GetBlogs()
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetBlogs(t *testing.T) {
|
func TestFetchBlogs(t *testing.T) {
|
||||||
blogs, err := GetBlogs()
|
blogs, err := FetchBlogs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no error, got %v", err)
|
t.Fatalf("Expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user