Chris Padilla/Blog
My passion project! Posts spanning music, art, software, books, and more
You can follow by Newsletter or RSS! (What's RSS?) Full archive here.
- /blog
- /blog/[tag]
- /blog/[tag]/[page]
Jet Set Radio Vibes
I give one listen to the Jet Set Radio Soundtrack, and now I get some funny 90s hip hop inspired ditties in my recomendations. Not bad!
Been doing lots of gesture drawing digitally to help get used to the pen. It's a surprising jump from traditional to wacom!
Lastly, I'm sorry to say that Anthony Clark bore the horrible news: #Warioctober has arrived.
I regret to inform you that today is the beginning of #Warioctober , where everyone, be they just or wicked, must honor the terrible creature Wario. Here is Classic Wario. May we be forgiven for what we must do. pic.twitter.com/i99DQTjToR
— Anthony Clark (@nedroid) October 1, 2023
I've tried to escape it, but the punishment for skipping out is much more fowl than the ritual itself.
Tourist's Guide to Go
I'm dipping my toes into Go! I became interested after hearing about how accessible it makes multi-threaded logic to coordinate, especially on cloud platforms.
I've been there with JavaScript and Python: it could be handly for processes running in tandem having the ability to communicate. Seems like Go opens this communication channel in a way that's easier to manage than a language such as Java. (All from hearsay, since I've not dabbled in Java just yet.)
I'm excited to dig into all that! But, as one does, first I'm relearning how string templating works in the new language:
Compile Time Errors
When getting ready to run a main.go
file, checks will be made to ensure there aren't any procedural errors. Go checks for unused variables, ensures types match, and that there are no syntax errors. A nice feature for a server side language, keeping the feedback loop tight!
Static Typing
Like C# and Java, Go is statically typed. On declaration, variables need to be assigned a type.
var numberOfCats uint
When assigning, the type can be inferred:
const conferenceTickets = 50
Variables
Constants and mutable variables exist here. Somewhat like JavaScript, the const
keyword works, and var
functions similarly to how it does in JavaScript. No let
option here.
A shorthand for declaring var is with :=
:
validEmail := strings.Contains(email, "@")
This does not work with the const
keyword, though.
Slices and Arrays
Arrays, similar to how they work in lower level languages, must have a set length defined on declaration:
var bookings [50]string
Slices, however, are a datatype built on top of Arrays that allow for flexibility.
var names []string
Both require that all elements be of the same type, hence the string
type following []
.
Maps
Maps in Go are synonymous with Dictionaries in Python and Objects in JavaScript. Assigning properties can be done with the angle bracket syntax:
newUser := make(map[string]string)
newUser["firstName"] = userName
newUser["email"] = email
newUser["tickets"] = strconv.FormatUint(uint64(userTickets), 10)
Maps can be declared with the make
function:
newUser := make(map[string]string)
For Loops
There's only one kind of For loop in Go. Syntactically, it looks similar to Python where you provide a range
from a list to loop through:
for _, value := range bookings {
names := strings.Fields(value)
firstName := names[0]
firstNames = append(firstNames, firstName)
}
Range here is returning an index and a value. Go will normally error out if there are unused variables. So to denote an intentional skip of the index property, the underscore _
will signal to go that we're ignoring this one.
Package
Like C# Namespaces, files in go can be grouped together as a package. You can do so with the package keyword at the top of the file:
package main
import (
"pet-app/cats"
"fmt"
"strconv"
"strings"
)
func main() {...}
Notice "pet-app/cats": here we can import our packages within the same directory with this syntax. "pet-app" is the app name in the generated "go.mod" file, and "cats" is the package name.
Public and Private Functions
By default, functions are scoped to the file. To make it public, use a capital letter in your function name so it may be used when imported:
func BookTickets(name string) string {
// Logic here
return res
}
Parkening - Spanish Dance
Seals EVERYWHERE!!
Migrating Blog Previews from SSR to SSG in Next
I've re-thought how I'm rendering my blog pages.
When I developed the site initially, I wasn't too worried about the difference between SSR and SSG. If anything, I wanted to lean on server rendering blog content so scheduled posts would load after their posting time passed.
Since then, my workflow has changed over to always publishing posts when I push the changes to GitHub. Posts are markdown files that are saved in the same repo as this site, so anytime a new post goes up, the site is rebuilt.
All that to say that I've been missing out on a juicy optimization opportunity with Next's static page generation!
So it's just as easy as switching the function name from getServerSideProps
over to getStaticProps
, right? Not quite in my case!
Page Structure
The pages that I'm looking to switch over are my blog feed pages. So that includes:
/blog is easy enough!
/blog/[tag] is the landing page for any of that tags clicked on either in a post or on my homepage. Tags could be what I have listed as "primary tags" such as "music", "art", or "tech." They could also be smaller tags, such as "React" that I have linked on individual blog pages, but that I don't list on my landing page or blog index page.
Some of those tags are now rendering content through pagination! So I have to take into account [page] numbers as well.
To get the full benefit of Static rendering with dynamic routes, I'll have to provide the routes that I want generated. Next has a fallback to server render requests that don't have a static page, so I'll exclude my smaller tags and focus in on the primary tags. I'll also want to generate the paginated links, measure how many pages need to be rendered per tag.
getStaticPaths
getStaticPaths
is the function where I'll be passing in my routes. This is added outside of the component alongside getStaticProps
to then generate the content for those routes.
For Tags Rendered as a List Page
This is pretty straightforward for tag pages. Just one level of looping involved:
export async function getStaticPaths() {
return {
paths: getBlogTagParams(),
fallback: 'blocking',
};
}
const getBlogTagParams = () => {
const tagsDisplayedAsList = ['books', 'notes', 'tech', 'art', 'music'];
return tagsDisplayedAsList.map((tag) => {
return {
params: {
tag,
},
};
});
};
For Paginated Tags
For music and art, it's one more level of looping and a few more lines of code:
export async function getStaticPaths() {
return {
paths: getBlogPageParams(),
fallback: 'blocking',
};
}
const getBlogPageParams = () => {
const allPostFields = ['title', 'date', 'hidden', 'tags'];
const allPosts = getAllPosts(allPostFields);
const publishedPosts = allPosts.filter(filterBlogPosts);
const res = [];
const fullPostPreviewTags = ['art', 'music'];
fullPostPreviewTags.forEach((tag) => {
const capitalizedTag = capitalizeFirstLetter(tag);
const regex = new RegExp(capitalizedTag, 'i');
let thisTagsPosts = publishedPosts.filter((post) =>
post.tags.some((e) => regex.test(e))
);
const count = thisTagsPosts.length;
const lastPage = Math.ceil(count / 5);
const pageNumbers = Array.from({ length: lastPage }, (_, i) =>
(i + 1).toString()
);
const tagSlug = lowercaseFirstLetter(tag);
const thisTagAndPageParams = pageNumbers.map((pageNum) => ({
params: {
tag: tagSlug,
page: pageNum,
},
}));
res.push(...thisTagAndPageParams);
});
const feedCount = publishedPosts.length;
const feedLastPage = Math.ceil(feedCount / 5);
const feedPageNumbers = Array.from({ length: feedLastPage }, (_, i) =>
(i + 1).toString()
);
const feedTagAndPageParams = feedPageNumbers.map((pageNum) => ({
params: {
tag: 'feed',
page: pageNum,
},
}));
res.push(...feedTagAndPageParams);
return res;
};
There's a fair amount of data massaging in there. The key point of note is that for each tag, I'm calculating the number of pages by dividing total posts by how many are rendered to each page:
const count = thisTagsPosts.length;
const lastPage = Math.ceil(count / 5);
const pageNumbers = Array.from({ length: lastPage }, (_, i) =>
(i + 1).toString()
);
From there, then it's a matter of piping that into the params object for getStaticPaths
Voilà! Now the site won't need to parse every blog post to render each of these preview pages! The static file will already have been generated and ready to go on request.