HN user

CodeIsTheEnd

910 karma

twitter.com/CodeIsTheEnd

Projects:

- plaintextsports.com: Live scores, play-by-play and box scores, in plain text. No images, no ads, no tracking, and no loading. (Also twitter.com/PlainTextSports)

- jless: A command-line JSON viewer written in Rust. (jless.io)

Posts11
Comments67
View on HN

Unsolicited feedback because I clicked: - Don't require picking a composition name before getting started - Whatever scroll settings you have broke swipe to go back, and then when I tapped the back button I got stuck in an infinite loop between the home page and the create composition page

My favorite line in my .gitconfig is an alias that prints out the commit history as a tidy graph. (The default `git log --graph` uses 6 whole lines per commit!)

    [alias]
        lg = log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all -n 15
Which I took from this Stack Overflow post: https://stackoverflow.com/questions/1057564/pretty-git-branc...

For sports, ESPN does have http://www.espn.com/nba/lite/scoreboard, but most of the links go back to the normal website. It may have been been more robust in the past, but I don't imagine anyone works on it anymore. It seems like the developer listed in the credits (http://www.espn.com/espn/lite/credits) last worked there in 2002!!

So if you're looking for a sports alternative, https://plaintextsports.com (which I made) works great! All the scores, play-by-play, box scores, standings, and schedules, but just no news stories. Blazing fast.

(No, it's not technically "Content-Type: text/plain", it uses HTML and CSS. Yes, I know it's not necessarily easier to read; it's an aesthetic. Yes, this is shameless self-promotion.)

A personal gripe with progressive web apps: they cache the website!!

A lot of people have requested that I make a progressive web app for https://plaintextsports.com. I've looked into it, but it fundamentally doesn't work, because it caches the home page. My website doesn't load any data via JavaScript; it just relies on the HTML page updating every 30 seconds, so when the home page is cached, the website just stops working.

I don't want this service worker nonsense. In most cases offline access isn't useful; people want the live scores. (It'd be moderately useful for viewing schedules offline, but I think most people use it for live scores.) I just want the website to show up as its own app in the app switcher, and basically nothing else to change: I still want the swipe-to-go-back gesture to still work. I still want the address bar with the manual refresh button. But I can't do that.

It's frustrating that they've provided a way to make a certain style of websites into first-class apps, but it doesn't work for the most basic websites!!

Side note: this is partially a the-best-way-to-get-a-question-answered-on-the-internet-is-to-say-something-wrong post. If there's a way to make it a PWA and not have all the pages heavily cached, please let me know! I would love to be wrong on this. (Unfortunately my noprocast just triggered though, so I won't be able to edit this post or respond for the next three hours.)

The intention is to put the schedules for everything on this page:

https://plaintextsports.com/premier-league/2022-2023/clubs/l...

The challenge is that these pages are really labor intensive, because I want to show relevant information for whatever league is happening, e.g., group stage points, leg of Champions League matches, and it's really difficult to figure out how to fit everything in just 45 columns.

I complained about it in this tweet: https://twitter.com/CodeIsTheEnd/status/1569489636900347904

I run Plain Text Sports (https://plaintextsports.com/).

I got sick of waiting for ESPN to load so I made a site with the goal of providing immediate access to what people actually care about: sports scores.

It's pretty fully featured at this point, supporting the NFL, NBA, MLB, NHL, MLS, Premier League and Champions League, college basketball and college football, WNBA, NWSL, and the World Cup. And for a lot of leagues it has full schedules and standings as well.

It actually was really popular in South Africa during the World Cup when a "super-app" that points users to sites that don't use a lot of data linked to it.

The site is served off of S3 (which is cheap), but there's a job running on an AWS server that's fetching the data for all the games and republishing pages to S3. It's using Ruby and is making a lot of network requests, so it's definitely not super efficient. I need a least a couple virtual CPUs, so it ends up costing ~$60 a month. I could definitely reduce that number, but the trouble just isn't worth it to me right now.

I don't want to make money on it. It's been difficult enough to get people to use it; getting people to pay for it would be even hard. At this point I feel like I've gotten everything I've wanted from the project. (An eye-opening experience into the marketing world; a website that I use every day.)

I try to limit how much I work on it now to mostly just maintenance work. I'd rather work on other stuff. If I started making money that'd just be a number to obsess over, and, honestly, it's never going to make enough money to replace my salary as a software engineer.

You are a hero and have just cured me of a hundred daily paper cuts. My employer should give you a bonus.

It works in Chrome, but unfortunately not in the standalone Slack client. (Maybe an argument for switching to the web version?)

(Uh, does anyone know how I select all (previously C-a) or search (previously C-f) in Chrome now??)

EDIT: Looks like C-/ functions as select all, still looking for a find replacement.

EDIT #2: Oh, silly me, as long as I'm not focused on an input, C-f still works.

For anyone using a Mac, the basic cursor movement commands available in readline and emacs—C-f, C-b, C-a, C-e, C-n, C-p—are also available in virtually any text input in any program, namely web browser inputs, url bars, and textareas, as well as all system UIs.

Once you get used to using these shortcuts that means you can compose and edit text anywhere without ever moving your hands.

I recently switched to using Linux full time for my job and it KILLS me that these shortcuts aren’t available outside of the terminal. I try to navigate to a search suggestion in Chrome using C-n and accidentally open three new windows. I try to add a sentence at the start of Slack message using C-a and accidentally highlight everything and delete it. I try to move my cursor up to edit a previous line using C-p and end up brining up a print dialogue. It’s breaking the muscle memory of my most basic interactions with the computer, and it sucks.

Alright, my bike-related tally over ~6 years in SF:

- 2 stolen bike seats (one by 4th and King, one in the Mission)

- 1 entire bike stolen (left out in the Castro overnight)

- 1 rear wheel stolen (in a parking garage by the SF Symphony)

- 1 seat post clamp (literally just the clamp they had to remove the seat, take off the clamp, then put the seat back; during a 30 minute piano lesson in the Mission)

But that doesn't included the most egregious thing that I ever witnessed: I was running up to Twin Peaks on a Saturday morning, and right around noon, in the mini parking lot between the actual two peaks, with dozens of people milling about, heard someone break a car window while there was someone in the vehicle and grab a bag. A woman was seated in the passenger seat and they broke the rear driver-side window. Within seconds a car pulled up with no license plates, the guy jumped in and they sped off.

PostgreSQL 15 4 years ago

The posts table also has a foreign key to the tenants table specified with ON DELETE CASCADE, so all the tenant's posts will be deleted. (I don't know if Postgres makes any effort to find some optimal ordering of the referenced tables—in this case deleting the tenant's posts firsts, then its users—to avoid updating records that will just get deleted anyway by cascading deletes.)

PostgreSQL 15 4 years ago

This release includes a feature I added [1] to support partial foreign key updates in referential integrity triggers!

This is useful for schemas that use a denormalized tenant id across multiple tables, as might be common in a multi-tenant application:

    CREATE TABLE tenants (id serial PRIMARY KEY);
    CREATE TABLE users (
      tenant_id int REFERENCES tenants ON DELETE CASCADE,
      id serial,
      PRIMARY KEY (tenant_id, id),
    );
    CREATE TABLE posts (
        tenant_id int REFERENCES tenants ON DELETE CASCADE,
        id serial,
        author_id int,
        PRIMARY KEY (tenant_id, id),
        FOREIGN KEY (tenant_id, author_id)
          REFERENCES users ON DELETE SET NULL
    );
This schema has a problem. When you delete a user, it will try to set both the tenant_id and author_id columns on the posts table to NULL:
    INSERT INTO tenants VALUES (1);
    INSERT INTO users VALUES (1, 101);
    INSERT INTO posts VALUES (1, 201, 101);
    DELETE FROM users WHERE id = 101;
    ERROR:  null value in column "tenant_id" violates not-null constraint
    DETAIL:  Failing row contains (null, 201, null).
When we delete a user, we really only want to clear the author_id column in the posts table, and we want to leave the tenant_id column untouched. The feature I added is a small syntax extension to support doing exactly this. You can provide an explicit column list to the ON DELETE SET NULL / ON DELETE SET DEFAULT actions:
    CREATE TABLE posts (
        tenant_id int REFERENCES tenants ON DELETE CASCADE,
        id serial,
        author_id int,
        PRIMARY KEY (tenant_id, id),
        FOREIGN KEY (tenant_id, author_id)
          -- Clear only author_id, not tenant_id
          REFERENCES users ON DELETE SET NULL (author_id)
          --                                  ^^^^^^^^^^^
    );
I initially encountered this problem while converting a database to use composite primary keys in preparation for migrating to Citus [2], and it required adding custom triggers for every single foreign key we created. Now it can be handled entirely by Postgres!

[1] https://www.postgresql.org/message-id/flat/CACqFVBZQyMYJV%3D...

[2] https://www.citusdata.com/

Shameless self-promotion: the homepage of plaintextsports.com is 5.2kb today [1], an in-progress WNBA game (4th quarter) is 11.2kb [2], and an extra inning MLB game is 8.8kb [3]. I wasn't aware of this size threshold, and I'm not at this level of optimization, but I'm always pleased to find more evidence of my playful claim that it's the "fastest website in the history of the internet".

[1] https://plaintextsports.com/all/2022-08-24/

[2] https://plaintextsports.com/wnba/2022-08-24/conn-dal

[3] https://plaintextsports.com/mlb/2022-08-24/laa-tb

I had never heard of GROUP BY CUBE either! It looks like it's part of a family of special GROUP BY operators—GROUPING SETS, CUBE, and ROLLUP—that basically issue the same query multiple times with different GROUP BY expressions and UNION the results together.

Using GROUP BY CUBE(a, b, c, ...) creates GROUP BY expressions for every element in the power set of {a, b, c, ...}, so GROUP BY CUBE(a, b) does separate GROUP BYs for (a, b), (a), (b) and ().

It's like SQL's version of a pivot table, returning aggregations of data filtered along multiple dimensions, and then also the aggregations of those aggregations.

It seems like it's well supported by Postgres [1], SQL Server [2] and Oracle [3], but MySQL only has partial support for ROLLUP with a different syntax [4].

[1] https://www.postgresql.org/docs/current/queries-table-expres...

[2] https://docs.microsoft.com/en-us/sql/t-sql/queries/select-gr...

[3] https://oracle-base.com/articles/misc/rollup-cube-grouping-f...

[4] https://dev.mysql.com/doc/refman/8.0/en/group-by-modifiers.h...

The most common constraint is whether the DB as a service offerings support a given extension, since they don't support installing custom ones. Naturally choosing to support an extension across a fleet of hundreds of thousands of instances (running dozens of different minor versions) isn't a decision made lightly, so it can take a while for new extensions to be supported.

Supported extensions on AWS RDS: https://docs.aws.amazon.com/AmazonRDS/latest/PostgreSQLRelea...

Supported extensions on Google Cloud SQL: https://cloud.google.com/sql/docs/postgres/extensions

Supported extensions on Azure Database: https://docs.microsoft.com/en-us/azure/postgresql/single-ser...

For college football and basketball I just use some JSON endpoints that the ncaa.com frontend hits, and they're actually very good. Literally all I had to do to support Women's basketball in addition to Men's was change "men" to "women" in the url. I haven't checked the data for baseball, but I'm sure it's more than adequate. That being said, I personally don't have any interest in college baseball, and I don't think it has a huge following, so I'm not going to invest any time into it, especially when there are higher profile leagues that I still haven't added yet (specifically, all of European football).

Thank you! (I made plaintextsports!)

On my to-do list is making a personalized page where you can select exactly which teams you want to follow, something closer to what dpeck wants, but I've been having trouble finding the time...

TIL: #method(:method_name) in Ruby

I'm a big fan of Ruby, and feel like I know the language pretty well, but, my goodness, I don't know how I've never come across this one before. THIS is something that feels like magic!!

    [1]> 3.method(:days).source_location
    => [".../lib/active_support/core_ext/numeric/time.rb", 37]

    [2]> puts 3.method(:days).comment
    # Returns a Duration instance matching the number of days provided.
    #
    #   2.days # => 2 days
    => nil

    [3]> puts 3.method(:days).source
      def days
        ActiveSupport::Duration.days(self)
      end
    => nil
https://ruby-doc.org/core-3.1.1/Method.html

EDIT: Wilfred IS the original author [3]; my apologies.

Not to discredit Wilfred (it looks like he's taken over the project as the maintainer), but, based on the historical contributions [1], it looks like it was originally developed by Max Brunsfeld, who also created Tree-sitter. [2]

[1] https://github.com/Wilfred/difftastic/graphs/contributors

[2] https://github.com/tree-sitter/tree-sitter

[3] https://github.com/Wilfred/difftastic/commit/958033924a2dea7...

Plain Text Sports 4 years ago

Hey, HN! Creator here!

I started building Plain Text Sports last year when I was watching an NFL playoff game in rural southwest Wisconsin. We had poor TV reception -- the image was going in-and-out -- and the local radio station was mostly static. I tried to check ESPN, but the loading bar was as frozen as the ground outside. I then had the idea for a website designed for the pure sports fan: no ads, no images, just scores, play-by-play and stats, with a simple information-dense display.

I initially added support for the NBA so I could follow my beloved Milwaukee Bucks. I posted it to /r/nba, got over 600 upvotes in a few hours, and got perma-banned for self-promotion. (I did a Show HN too, but it didn't take off. [1]) Over the following months I added NHL, MLB, NFL, college basketball and football, and added standings and team schedules so it could really be a one-stop-shop for my sporting needs. Just this past month I added MLS and NWSL (National Women's Soccer League). I plan on adding the WNBA and the Premier League later this year too.

Obviously I designed the site with minimalism and efficiency in mind, as a reaction to the bloated web we see today. We don't need heaps of JavaScript just to display a bit of text, nor do we need half-a-dozen sites tracking our "engagement", and our "retention". People just want to get the information they're looking for, as fast as possible. Technology shouldn't get in the way.

Despite the austere presentation, I'm really proud of the design of the site. As a commenter noted, it's not actually plain text, but does use some CSS and a tiny bit of JavaScript (sue me!). But there are a lot of small details that I put a lot of effort into: the game times on the front page automatically show up in your local time zone, and the boxes automatically expand to fit long time-zones. For the NBA, the raw play-by-play data I get is very granular. A steal, for example, is both a turnover by the offensive player, and a steal for the defensive player, but I combine those into a single event in the timeline. For the NFL, I draw an ASCII drawing of the field showing the progress of the most recent play [2]. When a team wins a championship, they get an ASCII trophy and a dedicated spot on the front-page for the next week. It's been really fun trying to figure out how to pack as much information as possible into a 45 column-wide display.

A streamer I watch on Twitch [3] who does marketing at Nvidia also had a competition amongst his viewers to make their own ads, and that led me down another rabbit hole of fun "plain-text" videos. [4][5][6].

Plain Text Sports also led to my next project. I get a lot of data from publicly accessible, but undocumented JSON APIs, and it was frustrating digging through giant JSON files trying to understand how certain situations were represented. That led me to build jless [7], a command-line JSON viewer, which made it to the front page last month.

[1]: Original Show HN: https://news.ycombinator.com/item?id=26310314

[2]: ASCII football field: https://twitter.com/CodeIsTheEnd/status/1436003783327293452

[3]: Atrioc, Nvidia marketing streamer: https://twitch.tv/atrioc

[4]: Never Miss Moment Ad: https://www.youtube.com/watch?v=9t1qY0vOJWc

[5]: Bucks Championship Run: https://www.youtube.com/watch?v=4WHcP4PTBHY

[6]: Wisconsin Sports Ad: https://www.youtube.com/watch?v=dLsm0MirOEg

[7]: jless, a command-line JSON viewer: https://jless.io

Is there a WYSIWYG (what-you-see-is-what-you-get [1]) document editor that is built on a foundation of HTML/CSS, and explicitly surfaces operations that map to CSS features, like flexbox? Changing the base style just means writing CSS rules for the `p` tag! And it could maybe even encourages component / class-based styling? I imagine it could be used for creating things that may get printed out, but will also see a longer life on a web page. You could even have explicit media queries to apply only when printing! [2]

I think of something like creating a good looking resume, which may include light graphic design elements like divider lines, and might not have a strictly linear layout and put some information in a sidebar. Making something look good in Word can be really frustrating, and require jiggering with margins and column layouts. It may fall apart when you try to add a new job. It's almost a joke that if you want a good looking resume, you should use LaTeX, but that's incredibly inaccessible. So many more people know basic HTML and CSS!

I think a lot of website builders (like Webflow [3] ?) expose a lot of underlying HTML/CSS, but I suspect they also support a lot more ad-hoc graphic design elements that can really make the underlying HTML document a total mess.

[1] https://en.wikipedia.org/wiki/WYSIWYG

[2] https://developer.mozilla.org/en-US/docs/Web/CSS/@media#prin...

[3] https://webflow.com/

No_color 4 years ago

Guilty [1]. But it's really hard to handle both light and dark color themes! On a dark terminal you're probably more likely to use the "Bright" variants when using the default 16 color palette, but then you'll have less contrast in a light theme. Some color schemes intentionally invert the "bright" colors and makes them darker for a light colored scheme. This is what Gruvbox [2] does, but most other palettes don't. I also have an issue with Solarized which makes some of the Bright colors almost exactly the same as the foreground or background, which can make text printed in certain colors seem invisible.

I've used ANSI codes to invert colors (which works pretty well for highlighting), and dim colors, but those can't solve everything.

[1] https://github.com/PaulJuliusMartinez/jless/issues/4 [2]: https://github.com/morhetz/gruvbox