A Good Enough Blog
Sometimes we write stuff.
You can read it here now, or later with the RSS feed.
-
/ Matthew Lettini / TIL
With conversation threading (which almost everyone has enabled), Gmail was trimming the bottom of the emails we send with our new app because they were too similar to the other emails in the conversation thread.
They’re similar because that’s just the link to get back into the app—you know, the important bit to keep our users engaged! Having that trimmed is bad.
The solution to this is silly: Gmail will not trim content if it’s always unique, so now we add a unique/random number to the bottom of every email we send. Insert eye roll.
-
Asking the Right Question
Lately I've been pondering the future for Album Whale. It does what we'd like it to do, and we've spread the word enough that there's now a steady stream of new users coming in each day. So, what's next?
Portrait of the author pondering My first impulse was to answer this question: How do we get people to come back and spend more time on Album Whale?
Following that questions, I came up with a list of ideas on how to get people's attention so that they'd use Album Whale more often. But something didn't sit quite right with me, and it took me a whole night of sleep to figure out what was wrong.
-
Season 3, Issue 2: Britney Coltrane
1. I ❤️HR
The past two weeks Barry and Lettini have been building our next little project, and I tried to deal with the company-side of work: setting up payroll, health insurance, and making sure we’re compliant (hello, bureaucracy!). Back in the day when Danny and I started Iridesco, we had to get on the phone (this was before the iPhone and we had a real telephone) and call people—like a health insurance broker—and fill out a bunch of paperwork.
It’s a different world today and now there are apps for all this HR quagmire. We’re using Justworks, which seems good enough. It’s impressive how they’re able to conceal most of the stink of the rotten business of health insurance (such a shitty, gross, and prohibitively expensive product).
-
Good Enough, A Serious Business
Hello reader, my name is Shawn, and I'm one of the co-founders of Good Enough. I'd like to tell you a bit about how Good Enough came to be and what we plan on doing here.
Barry and I started working together again in early 2022. By “work,” I mean that I’d show up on Slack for a couple of hours each day, make some wise cracks, try to design in Photoshop and give up, and once in a while wrote some poor HTML and CSS. (Barry probably worked a lot more––somebody had to do the heavy lifting!)
Our first public project was DoEvery.Day, and then we didn’t do much for the rest of the year until we met up again in the fall to work on Album Whale.
For me, this half-ass effort wasn’t cutting it. I wasn’t proud of my work, and I felt like I was often not there for Barry. I felt like I had to make a choice: either leave Good Enough and go back to making Neophyte all day, or take it more seriously.
-
/ Barry Hess / TIL
I am working on a new product that's a bit more complicated than our prior releases. Getting refamiliarized with nested parameters in Rails has been interesting. In this case imagine I have a discussion that, when creating it I also want to create the first comment. The discussion information is in a form with a nested form for the comment. So at the base you have these permitted parameters:
params.require(:discussion).permit(:title, comments_attributes: [:body])
That is fine, but I wanted to assure that the discussion and comment couldn't be messed with and would always be associated with the logged in,
current_user
. Stack Overflow told me this would be pretty straightforward these days. Unfortunately that didn't work for me. Perhaps I'm doing something wrong, but I had to get a little more…into it.def discussion_params
_discussion_params = params.require(:discussion).permit(:title, comments_attributes: [:body])
_discussion_params.reverse_merge!(user_id: current_user.id)
_discussion_params[:comments_attributes].each do |key, value|
_discussion_params[:comments_attributes][key] = value.reverse_merge(user_id: current_user.id)
end
_discussion_params
endI'd be happy to hear that there's an easier way.
-
/ Barry Hess / TIL
I am working on search for Album Whale and I was very confused when my JavaScript fetch requests were not resulting in turbo_stream rendering getting displayed on my page. I checked my logs to see if the render was happening. I looked for missing
<%=
tags. Debugged andlogger.info
'd all over.Then I finally noticed the following in my Rails logs:
Processing by AlbumsController#search as */*
Oh, I expected that to say something about
TURBO_STREAM
, like:Processing by AlbumsController#search as TURBO_STREAM
This sent me on a dive into headers (I didn't have an
Accept
header on myfetch
request), response handling, and the fetch API.Here's what I came up with for our first rough cut of a working, live search field (this is Stimulus code):
async searchForAlbum() {
let queryString = this.queryStringTarget.value
if (queryString.length > 2) {
fetch(this.urlValue, {
method: 'POST',
body: JSON.stringify({
"queryString": queryString
}),
credentials: 'same-origin',
headers: this.getHeaders()
})
.then((response) => {
if (!response.ok) {
alert("something went wrong!");
window.location.reload(true);
}
return response.text()
})
# Here we need to pass the response to Turbo for processing
.then((html) => Turbo.renderStreamMessage(html))
}
}
getHeaders() {
let headers = new Headers()
headers.append('Content-Type', 'application/json')
# Here we want a Turbo response
headers.append('Accept', 'text/vnd.turbo-stream.html')
headers.append('X-CSRF-Token', document.querySelector('meta[name="csrf-token"]').content)
return headers
}I suspect there's a more succinct way to do this, but I'm setting it down for now.
-
Season 3, Issue 1: Whale, Hello There!
Dear friends, we have not been good at writing this newsletter regularly. But writing is hard work, and there’s only so much time we have, and there was the winter break and Shawn kept getting sick—we have many excuses, how much time do you have?
-
All About CSS: Alphabetize, Normalize, and Dark-mode-itize
Hello reader, I’m Matthew, the newest member of Good Enough (LLC). The rest of the team are avid writers and sharers… I’m not. But they keep chanting “One of us. One of us.” so I suppose you’ll see me around here sometimes.
For my inaugural post, I thought I’d quickly share what I did in my first few days to win their affection.
-
/ Barry Hess / TIL
I had to figure out how to run browser-based tests in Rails again. In part that meant working around my lack of Chrome. Rails System Tests with Safari.
I also had some problems with Rails Turbo Drive taking over EVERYTHING, which required me to figure out how to deal with a Rails form redirect not rendering HTML.
-
We noticed that the victory symbol at the bottom of our homepage looked different in Chrome than Safari––it was playing smart and rendered the unicode character as an emoji. Lettini found a fix though! Append this string of code to the symbol:
︎
.So
✌︎
renders to ✌︎ and not emoji. Weird.(Here’s a blogpost that explains it all.)
-
/ Barry Hess / TIL
DoEvery.Day calendar navigation was broken. We built it using Turbo Rails as it was being developed. As we have updated libraries, including Rails, the API has changed. In order to get the calendar navigation working again, I had to pull out some old hacks that made use of
turbo_stream_action_tag
via Turbo Streams to get my Turbo Frames working. The code is a bit more straightforward now, though I find Turbo Frames to feel a little too magical.Turbo Frames Vs. Turbo Streams gave me my first hints at how to fix things up, but the example request flow was a little too odd for me to absorb. Turbo Frames and Turbo Stream templates ended up explaining things well enough that I could get the issue resolved.
I ended up in this PR to re-discover how a Turbo-Frame-based request can update the browser’s URL. In this case that’s
data-turbo-action
, which in practice looks something like:link_to("My Link", my_path(data: { turbo_frame: "tf_calendar", turbo_action: :advance })
-
Season 1, Issue 5: Promotes Regularity
Good Enough is taking a summer pause! We each have travel plans lined up for the summer. We each have at least two children in our homes, begging for attention. And we each truly appreciate extended breaks as a means of reinvigorating ourselves to the ever important task of building bemusing things. So while you definitely shouldn’t expect anything from us ever, especially don’t expect any more newsletters over the summer. As the Nintendo Wii would say, why not take a break?
-
Season 1, Issue 4: Issue 4-evah
0. It’s Been a Long Time
Sorry 4 the silence these past weeks. Life happened fast and furious. Barry’s daughters all had marching band, and he chaperoned 4 the parades. His eldest daughter also went and got a boyfriend. Shawn’s been feeling a little under the weather, and his two boys graduated. His sons haven’t gotten a girlfriend or boyfriend yet.
Get used to it as there won’t be a newsletter next week, either. It’s good to keep expectations low.
Now on to 4 things 4 this 4th issue.
-
Season 1, Issue 3: Fool Me Three Times
This will be a short one. Barry’s summer vacation has started, and Shawn is deflated after the Warr…iors... sob… It’s just one game, right?
1. No One Likes a Giveaway
Who doesn’t love a good pickle? Well that didn’t happen. There were no entries to last week’s giveaway. If you recall the rules were (1) tell someone about this newsletter and (b) leave a comment saying as much. As my mother-in-law says to me literally every time I see her, “With friends like these, who needs enemies?” —BH
-
Season 1, Issue 2: Just the Two of Us
0. Everyone Likes a Giveaway
Stick around to hear how you can win a fabulous prize by hardly doing anything. Take it away, fellas…
1. Still Positive
Did you know that once you acquire COVID-19 your PCR test can be positive for up to 90 days? Did you know that the at-home tests can also be positive for up to 90 days? So if you’re ever trapped in your bedroom hoping to get an early exit for, say, your daughter’s massive graduation party, don’t think you can negative test your way out of there. The bouncer will not (or should not) accept your ticket and will turn you away. —BH
-
Season 1, Issue 1: The Loneliest Number
1. Confirmed Positive
After two years of running from reality, it has finally happened for my family. I have tested positive for SARS Coronavirus-2, the virus that causes COVID-19. For this honor, I would like to thank our society’s collective tiring of virus mitigation. It truly takes a village. —BH
This week we are celebrating family birthdays and child graduations (Barry will probably be missing his eldest’s graduation party 😭). Life has taken over and said, hey, maybe don’t plan on getting too much done this week. Shawn is working on the next issue of his zine, Neophyte, while Barry has been working on his personal website to a concerningly addictive degree. We also wrote this newsletter, which has quickly become our favorite tool for procrastination!
-
Issue 0: Collector's Item
1. Oh, hello.
We didn’t see you there. How are you doing?
You’ve somehow ended up with a collector’s item in your hands: the very first Good Enough Newsletter. This is the beginning of what we (Barry Hess & Shawn Liu) hope to be a years-long series of communiqués with our rabid fans. We call our fans rabid because there are so few of them. Animals might roam in packs, but rabid animals? Well, they’re usually out on their own, attacking innocent bystanders.
(Our fans wouldn’t attack anyone, though, would they? Who’s to say.)
More to come.
Subscribe to A Good Enough Newsletter to stay tuned.