Coding with Cursor: Rails vs Next Session 1

Sahil Lavingia and Tony build a pickup game app side-by-side using Rails and Next.js to compare developer experience. Rails scaffolds a full admin UI and server-side rendering quickly with HTMX, while Next.js requires more setup but offers modern component libraries like ShadCN. Both use Cursor AI to generate code, but encounter hallucinations and attribute mismatches that need manual fixes.
Chapters
share my screen with you on Google Hangouts, right? And then you stream that, presumably?
Exactly. Like, you don't have to really do much. It's kind of just like a Zoom meeting. And, and then basically, if you share, it'll show up, and then I'll share. And basically, most of the time, it'll probably just be kind of like both of our faces.
Okay.
But yeah, I think it'll be fun to do a few checkpoints where we can just kind of go through like what we actually got done.
Um, yeah, awesome.
I think, um, oh, have you started streaming? Nice.
Yeah, I think so. Should people jump in? YouTube or Twitter?
Like, where do I see it?
I put it on YouTube and then I tweeted about it.
Okay, hasn't shown up yet. Here we go.
Let me just retweet that.
Why is my video being annoying? Okay, this is not showing my face for some reason. Resume.
There you go. Awesome.
Yeah, people will slowly jump in, but the, yeah, most of the viewers will be on the, will be after the fact.
So welcome everybody. We're gonna do a stream. A few weeks ago, I guess now, I tweeted about how Ruby on Rails was a form of technical debt, which was a bit loaded, uh, in hindsight. And, uh, it sparked some good discussion though, especially some offline ones, including one we hung out and actually in this office right over there. Um, so Tony came by and, uh, showed me how awesome HTMX is actually. And so I thought it'd be fun to, yeah, just show people actually like what, what building something new looks like. I think the caveat is that, you know, at the end of the day, You're not gonna learn if Ruby on Rails is technical debt or not, in the, in the sense that, you know, it takes years and years of building stuff to really get to that point. But hopefully at least we can show people some of like the kind of developer experience, user experience differences, and sort of just maybe the approach that, you know, people would take building stuff in either language. And also show people that honestly you could use both, like Rails is great, so is Next, depends on what you wanna build and what your preferences are. All that kind of good stuff.
So we'll build, we'll build something and, and see how it goes.
And maybe, maybe if there's enough people on the stream, we can like do a vote or something and declare a winner.
But we're both winners here.
We're all winners here. Anything to add, Tony?
No, I think you kind of covered it. I mean, my background obviously is Rails. I've been building with Rails for 10 years. And Yeah, I think that if you know how to do stuff, if you figure out what your patterns are and what your kind of libraries and the way that you do approach things is, then you can go very, very far and you can do a lot. So obviously I took issue with you saying it was tech debt. I'm just excited to show, hopefully show it actually can be done. And yeah, we're both using cursors, so it'll be interesting to see the pros and cons of each approach.
Awesome.
Cool.
So yeah, we're going to build a pickup game kind of app, web app marketplace where you'll be able to see pickup games. Maybe we can do some filtering and searching or something like that.
But basically, you'll be able to see pickup games, maybe create them, see them.
If you're logged in, you can delete the ones that you've created. You can join them. And yeah, we'll see how far we get. But there'll be some backend, some frontend, some user experience, some HTML, CSS, and JavaScript, regardless of the stack. And then everything else will be custom. But that's basically the rules. I mean, there won't be too much more than that. And we'll just build stuff. And then maybe every every 15, 20, maybe every 30 minutes, probably we can do like a check-in. So we'll do like 4 check-ins, 3 or 4 check-ins over the next 2 hours and see it, see how much progress we make.
Do you want to do it where we're both coding at the same time but one screen shared, and so when we switch over, the other person will have already done? Or do you want to each pause while the other person is actually doing the work?
Well, that's a good idea. Let's do— yeah, like one of us will We'll screen share, we'll screen share at a time. Yeah. And then we'll basically in that transition point, we can kind of say, okay, this is what I got done so far.
And then we can, we can pass it over.
Okay.
And that should work pretty nicely.
So I guess I'll, I'll probably start if, if you're cool with that, or actually, do you want to start or what, what do you think is the best? I feel like you, because I feel like there's so much Next content of like how to get started.
I feel like you streaming would be awesome.
Yeah, I'm happy to.
Yeah.
Yeah.
Awesome.
Let me share my screen though.
So let me make sure I'm sharing the right one.
You see my screen?
Yep.
You're seeing the one with GitHub on the right-hand side, yeah?
Exactly, yeah, GitHub on the right-hand side.
The first, just to set a bit of context, is, yeah, I mean, my approach, I have this website called HTML First. Like, I like to focus on the basics, focus on the platform. I really like code bases that are as close to the platform as possible. So as close to the kind of the bare metal, because I mean, I just like being able to hire teams and train people up very, very quickly. So that's kind of what I've been optimizing for over the last couple of years. So I have a boilerplate here that we've put together for the agency. So first thing I'll do, let me just get Cursor open here as well. So sorry, 2 seconds.
Open up a terminal.
No, you know what, I'll do it down here because I want to be careful not to share anything I haven't done.
There's always a risk.
I like hidden everything. Documents.
Sorry.
Just going to open this up here.
I should have actually got this ready before the screen share started.
This is reality. Yeah.
So I'll fork something and get started too.
Okay. Where is this? Tony Hanna's Projects Playground.
Okay.
Sorry, I know you can't see my screen. Give me 2 seconds. Okay, git clone, clone this repo into—
what do we call this?
Pickup app.
Pickup app.
Okay, and now I'm just going to open up a new cursor window. Here we go. Playground demos pickup app.
Okay.
This is a fresh Rails install. First thing I'm gonna do is just run bundle install. I presume like going from fresh was the goal here, right? Just to show people what the first run looks like.
Yeah, totally.
Okay.
So let's start. Start a server, start at 3002.
Okay, so this boilerplate obviously comes with a few little kind of goodies that we've picked out over the last couple of years. So the first thing that we have is a thing called Active Admin. So what I'd normally do when I'm starting, and I would open up the admin panel, then I open up Plecticat, which is a little diagramming tool, and I would begin to just mock out the data model. So for pickup game, and again, like, Sahil, feel free to jump in here and like push me in the right direction if you feel that this is not relevant, go in a different direction.
So no, this is good.
Yeah.
Okay, good.
So we've got games, we have users, a user would be attached to a game through registration.
So game's going to have a location as well.
Oops.
Location normally is going to have a name.
I actually had a quick look before the call started, and there's one app that I use in Amsterdam which I quite like, which is called SC Urban, which is very similar. It's just like a list of games. So I think I'm just going to use some of this as a template. So game is going to have location. What else does a game have? Start and end, which is a DateTime.
Description will give it—
you can give a game a name as well, like a title.
Yeah, let's leave that at that.
And then users are gonna have the normal stuff. So this app actually already has a user model. Again, Rails kind of comes— Rails 8 comes bundled with a lot of that user stuff. So there's already a user model in the app, which I'll give you guys a quick look at now. I'm just gonna zoom in on cursor as well, actually, here.
Yeah, if you want, some people often complain about the font size.
That's like the number one complaint with these things.
So, okay, so we got users, we got email, first name, last name.
Again, this is a little bit boring. Try and get more engaging now.
And then a user is attached to a game through registration, and then you're gonna have a, I suppose it's like an answer, which is like yes or no. Suppose— no, you know what, let's just leave that as a Boolean.
If they're registered, then they're registered. And we'll just have that as a Boolean.
OK. OK, so the first thing I'll do is I'll mock up the models. So Rails follows MVC, an MVC pattern. So you've got a controllers folder, you've got models folder, and then you've got a views folder. And then the other thing to be aware of is the routes file.
File in config/routes.
You can see here I already have some routes for user signup, but what I'm going to do is I'm going to create these models. Now, I actually was going to use Cursor to do this, but I honestly think it's literally quicker to type this out because of the CLI command. So I'm going to jump into using some AI stuff just afterwards. But for just to create the models, I'm just going to use the normal CLI. First thing I'm going to do is create the games model.
So give it a location ID.
That's gonna be an integer 'cause it's referencing another table.
Start, datetime, end, datetime, description text, and the name text as well. I think that's fine.
So once you've created that migration, Rails works with migrations. So once you create the migration file, it turns up in db/migrate.
Where's db/, there we go.
It creates this file called create_games. Then to run that migration, you run rails db:migrate. That's created our games table. This is where I can actually just show you what we use this Active Admin backend. I'll just show you how that works and the benefit of using that for spinning up apps really quickly. I'm going to create a resource called game, an Active Admin resource called game. What that does is creates a file up here in the admin folder. Which just registers a game. Then if I open up the admin here and I sign in, now you can see I've got users was already there, but now I've got games here as well. That model that I just created that has attributes is already exposed here. This is nice and handy for just populating the app with data. First thing I'm gonna do is I'll just allow some of these attributes. So this is the first shortcoming, I think, of Cursor with Rails is that it's not that good. You were talking about things being fully typed. Sometimes Cursor will predict attributes that don't exist. So we have an attribute called start and end, but it was trying to autocomplete to start_time and end_time even though they don't actually exist. So that's one shortcoming. But to be honest with you, I've played around quite a bit with it and I think that's gonna get a lot better. Because we do have a full definition of our entire database in a file called schema. So once Cursor is aware of what's going on in here, it should be good enough at predicting what those attributes are.
So I'm just going to make this form a little bit cleaner because name is a huge text area here, so I don't want it to be a text area. Description's text. Yeah, let's make that. Okay, let's leave start and end as they are because I think the defaults are fine.
Oh, you know what, location is not going to work yet because we haven't created it. So I'll leave that commented out. OK, so now we have a little panel to create a new game. Now let's create the location model.
Let's jump back over again to the Blacktica. So rails g model location.
I'm just going to give it a name. And then what I normally do with locations is I store serialized. I use a thing called Radar.com for It's like a Google Maps alternative. So I'll just store like a serialized JSON of all of the information about that location. So I'll just call it serialized data, which is a JSON. We're using Postgres here.
Actually, no, we're not.
We're using SQLite because this is local. So that's not going to work. I only recently switched to SQLite. So let's just store it as text. But we can actually, we can put JSON in there anyway.
Okay. So that's that model created. Database migrated and then let's, oops.
Sorry.
Keyboard shortcuts on cursor get in my way sometimes.
And then let's do the RailsG ActiveAdmin resource.
Location. So now when we open up our backend here, we've got location.
Okay, great.
So again, I'm just going to quickly open this up and clean up the attributes a little bit here. So, yeah, okay, that's super nice.
That cursor, like, cursor is way faster, I find, than Copilot for that kind of stuff, just the autocomplete stuff.
Yeah, super good.
And then let's do that. Permit params.
Okay. So again, just using— I'm just kind of going stream of consciousness here, but just using this as an example.
Yeah, let's—
I'm in San Francisco right now.
So what are some—
I like soccer.
I play soccer because I'm European.
So let's see what soccer fields we have in San Francisco.
No, doesn't look like it's the bridge. Okay, I'll just—
not to get distracted, I'll just poke it out and park it. Okay, so I'm not going to put in serialized data just yet. You know what actually would be nice as well is to have an image for a location because these ones do actually have images. So, and this is kind of relevant as well just to show how Rails does images. So Rails has a thing called Active Storage, and with Active Storage you can basically define on the model. So we've got a what's it, location model. Um, you can go in the model and basically just do has many attached or has one attached image. Um, now you need to configure the storage adapter. So if you're doing this on production, you're going to be connecting to S3 or R2 or something. We're doing this locally, so it's just going to store the files locally. Um, that I think is actually everything we need to do. So now I I'm pretty sure I can go into this and create a new input for image as a file and hit edit.
Yeah, and then I get prompted to have an image. So just going to get a random image here.
Okay, this is an American football stadium, but we'll use it anyway.
Let's save it in desktop. Okay. And then let's try and upload it.
Oh, hang on.
Sorry.
It's actually not going to work until I permit it in here.
Okay.
Location. Great.
So that looks like it should have worked now. One thing is we want to actually see it. So here's where I actually would use Cursor. So I just, if I don't know how to do a thing, I would just hit Command K and then obviously ask it.
So Show the image if it exists. Generate. Nice. Yeah, that looks, looks fine. So I can remove the actual line there.
There we go. Okay, so now we have a location that has an image and there's all sorts of stuff you can do with variants and resizing these images and cropping them and stuff. But this is kind of it. In its basic form. So that's the— I mean, that's what I would normally do with the backend. Oh, hang on. Yeah, let's jump over to the registrations. So let's create the registration model.
So rails g model registration.
It's gonna have a user ID. So the way that we'll do this is where a user— you basically can't register for a game unless you're registered as a user in the system, just to keep it nice and simple for now. So the registration model is going to connect the user to the game. So all it needs to have is a user ID, game ID, and just call it registered.
Just call it a boolean.
Okay. I just remembered actually another attribute we should have in the games table is max capacity. So obviously you don't want too many people to be playing.
So if it reaches a certain number, then it should prevent you from adding more people.
So let's do that as well.
Let's go back and do that in a sec. Create the registrations model, migrate the database, the active admin resource. Okay, that's an active admin. Great.
I'm also gonna— now that these models are created, we do need to tell Rails how they relate to each other. Again, I think autocomplete's gonna work pretty fine here.
So game belongs to—
does a game belong to location?
Yeah, it does.
A location has many games.
Registration belongs to a user. And again, boom. And then a user, we're gonna say has many registrations, has many games through registrations.
Great.
So this is, in my opinion, the best part of Rails, which is ActiveRecord. So what we're doing, we're in the models, which are powered by ActiveRecord, which is an ORM. An ORM is a layer that sits on top of a database and allows you to kind of interact with that database in a syntax that's slightly nicer and more secure than just raw SQL. So this is what you're seeing here. Everything in the models folder is how Rails does database models. So you create a model for each table effectively, and then you can add the relationships in here. And then you can also add validations. So for example, you can say in the game, we never want to have any games in the database that don't have a name or a start or an end. So you can just do validates présence_of, location is a model, so remove that. But yeah, this is how ActiveRecord works. And so now I think we have created all the backend. So let's load this up. We've got games, locations. Let's just create a game. I'm just checking the clock, Sahil. How am I doing for time here before we start?
You're doing good.
Yeah, we can switch over maybe like 3—
let's do 3:45 or something like that, maybe.
Okay, cool.
So do another 20 minutes.
Okay, so create a game here called Monday Evening Pickup, uh, admin games. Um, yes, so these parameters are permitted, great. Um, I think it'd be nice to add rich text here, but I'm going to expose the rich text on the user side because this is just the admin panel. This is like the thing that Yeah, you just— it doesn't really matter what this looks like because this is kind of used by internal staff members of the Pickup app. So I'm going to expose some nicer stuff on the, on the actual user side. That's why these date pickers are kind of crummy as well. Like, that's pretty, pretty janky. But let's create a game. Also, I'm thinking that you should actually have probably a model called GameInstances here because you want to have a game for— the game is on the specific date, but then you might have recurring games. So that could be something that we could add later on. And if you add a recurring game, it'll kind of create the data in the database for the upcoming games. But let's skip that for now.
So that'd be—
yeah, that's like an advanced feature.
Yeah, yeah.
I recently built an airline and it's very similar. So like figuring that data model out took a while, but I think we can use the same, the same idea, but we'll skip that for now. So we have— let's go there. What date is today?
7th of October.
Say the 9th of October at 6.
Man, this is slow.
There are better, obviously much better components than this. This is not indicative of Rails generally. You can use date picker components and stuff. It's just for pure speed here and to keep things engaging. I'm just going to use what comes out of the box.
So let's create a game.
Did that create?
Oh, what happened there? Let's have a look at the logs. Normally that would just— why did that not redirect? Hmm, interesting.
We may have hit our first snag. Oh, you know what's going on?
I think that the game model has some— yeah, belongs to location optional true, because I'm not actually sending a location in here.
So it's basically not saving. Now normally we'd show errors here, but again, normally I'd do that on the front end.
Yeah, okay, there we go.
In fact, actually, just let's actually just add location because we do want the game to have a location. That's going to cause issues on the other side.
So admin game. Oh, there we go.
It's already commented out.
So let's see if that works. Location, Glen Park.
Great.
Okay.
So now we have a game in the database, a location in the database. So maybe we switch over and do the frontend. That'd be a good thing to do, Sahil, or what do you think?
Yeah, that sounds good. I mean, you get all this stuff for free. It's amazing.
The admin portal stuff that I feel like Next is not very good at providing you that stuff.
Yeah, yeah. Definitely like backend strength versus frontend strength.
Yeah, 100%.
And to be fair, this thing ActiveAdmin, it's been around for a long time. And I think there's other better ones. Well, I wouldn't even say better. There's other newer ones now. There's one called AVO. There's a couple of them. But I have just always ended up coming back to ActiveAdmin because the DSL is just so succinct, so neat. And it is actually still actively maintained. So they're about to launch a new version of this. You can see the UI is quite dated. They're just about to launch a Tailwind version, which is like version 4 of ActiveAdmin. So it's much nicer to kind of work with layouts and stuff. But yeah, it's super nice. As you saw, like very, very quick to get this basic scaffold mocked out. Okay, so let's open up a new file and just like do a brain dump of what I'm thinking for the user side. We obviously want a place to list the games. Ideally on that we can register for a game. So you can like one-click register button on the list of games, register for game.
And just say check capacity.
Maybe we want a section where you can actually manage your own pickup games. What else? Yeah, let's just start with that for now. So what I'd normally do, okay, this is, we're gonna be using a lot more AI here as well. So I have a cursor rules file. Again, this is one of the not places where Rails is not as good as Next for this because it's not fully typed obviously. So I have a cursor rules file that basically gives Cursor a bunch of instructions on what to do and what not to do. So what I'm going to do first is, you know what I'll do actually, I'm going to, yeah, let's create a controller. So I'm just going to open the command prompt down here.
So please create for me and just give it a list.
So I want a route at forward slash, actually, no, I'm going to do that the root route because I'm just going to have it be when you come to the home page. That's just gonna show you a list of games.
So a list of games, and I'll just say games index.
And then we want, what else did we have in that list? Register for game.
Oh, let's leave that for now.
You know what, let's just start with this. Root route that shows a list of games, so corresponding view.
That shows games including— what do we want to show? We want to show the title, so the name, the start and end. We want to show the location name and an image.
Actually, I'll put the— I'll do the image after because I want to keep it simple and do it incrementally. So start then.
Oh yeah, and then registered users to the game.
And just from a UI perspective, I have a few components. So I'm going to say put each game in a UI-box. This is where I think everyone should be like— I've been a huge fan of Tailwind for a long time. I actually built a thing called Tailwind Lite, which is a buildless version of Tailwind. But the more I began to work with Cursr and with AI-generated, AI-assisted programming generally, the more I found that consistency with Tailwind is tough because it is so, it's so atomic. So when you're generating Tailwind classes, for Cursr to know that you've added whatever p-5 on your homepage and to not add p-7 on an internal page is tougher. So I've actually gone the way of building a very, very lightweight set of classes that I can use and reuse. We still use Tailwind a lot for spacing and layout, but we have a set of classes that we use here as well. So that's UI box is one of them anyway.
Bring them back in.
So let's—
oh no, I've done this before as well. I don't want that to generate.
Ah, so how do I get that back?
Do you know?
Is there a way to do that? Get what back?
I just lost my prompt because I Ctrl+C'd it.
Oh, you can sometimes you can do Control Command Z and it will kind of pop back if you—
but not always.
No, it's not. That's so annoying.
So this is one of those up.
Yeah.
No, I don't know.
No, because I actually didn't want to generate in the terminal. So this is one of the frustrating things where, yeah, I don't want— what is it? Command I. Yeah, because this looks so like— so see this, the terminal one looks so like the general cross-app one?
Yeah.
I'm going to have to do that again. Okay.
Sorry, this is kind of boring. So create for me that route at the root that lists games.
Is this— Sahil, out of interest, like when you're generating stuff, would you generate at this level of like zoomed outness or would you kind of go controller at a time or view at a time?
What would you— how would you do it?
Normally I go controller, like file by file. But it depends. I'm trying to get better at using the Composer stuff, but I just find it faster for me to just like in my brain to just go controller, model, view and kind of do it that way.
Okay.
Gotcha. Yeah.
I'm literally the exact same. So, I'm trying to get better at— since Cursor has exposed this like multi-file stuff, I'm trying to get better at using it. So, we'll see how this goes. Interesting experiment.
So, a corresponding view that lists games and for each one shows, let's say, name, start, end, location info, and then registered users. I'm gonna say put each game in a UI box.
Okay.
Now let's see how we do this.
Okay, so first it's modified the routes file. So let's jump over and see what it did.
Nice.
Yeah, very simple to read.
So just add forwards—
oh, hang on, actually. No, that's not how Rails does routes. Um, in fact, has it overwritten my routes file?
No, it hasn't. So why is it—
do you know why it does that?
Why does it not show the rest of the code?
It's really annoying about it, but sometimes it works if you hit accept. It just does that in the preview.
Okay, well, let's—
yeah, okay, it's kind of a fancy way of doing it, I think.
Yeah, okay, so what I'm gonna— I'm actually just gonna change this because we already have a routes We already have a route, a root route. So games index, let's have a look at the games controller created. Yeah, so this is quite nice. And this is, this is one thing that I think Rails has over like a separate SPA on the front end and the back end. So Rails ActiveRecord knows about your data models. And so it can do, it can basically avoid n+1 queries. Like one of the places where your app's going to get slow is you've got a game and each game has a location and each location has another related model and all of a sudden you're doing 100, 100 database queries for each page. So this is smart enough to know to include information about location registrations on user in the single query. So all this is the MVC model. The way it works is you've got— this is your controller, which is the C, and it just fetches the data. And then in the view, you're going to show the data. Now the views are, as I said, I like to keep it as close to HTML as possible. So This is the view that it created. It looks like it's using the— these are all classes that exist in my little stylesheet. That's good. UI box, games, each game name. Interesting.
Well, let's see. Let's just go over here and see what it has generated for us. First issue. Yeah.
Now I actually— this is something that keeps popping up and it's the most frustrating part of using CursorWithRails is it will hallucinate attributes that don't exist. So the game location does not have an attribute called address, and yet it's trying to render an attribute called address here. So I'm just going to remove that entirely.
In fact, I actually didn't tell it to render address, so I don't know why it is. OK, here we go.
So we have a list of games. We have the start date and end date.
No users registered yet. OK, cool. Let's add a register button here. This is, this is going to be a very interesting test.
Okay.
So on my games index, actually, you know what I'm going to do as well? This should be signed in. So users shouldn't be able to see this if they're not signed in. Oh no, it's fine actually. They just, we just won't show the button if they're not signed in. So, okay. On my games index.
For each game, add a register button that will— if I'm—
so I say not show. So don't show if not signed in. If registered.
Show a deregister button.
Because again, I'm just thinking again how this works generally, say.
And then if not registered, show a register button.
And then I'm going to ask it to add the corresponding controller.
Route controller for registering to a game.
Yeah, this could go very wrong, but let's give it a shot. Toggle registration, I like that. So basically you can use the same route for registering and deregistering. Okay, update the games index. I'm just going to YOLO this again, just in the interest of expediency here, and just accept all and see what happens.
Hope for the best. Let's reload. No, okay, first thing is, why did it now add in attributes that don't exist? Because that one is actually fine. So what is the attribute on a game? It's name, not title.
Okay.
Nope, date.
So annoying.
That's actually nice. So let's show the date in a nice format.
Okay, I don't see a register button. Ah, I'm not signed in.
So I already have some routes here to sign in. So what is it? User sign in?
Yeah.
I'm actually just gonna create an account here. I think that already exists, so. Okay, oh, interesting.
So now we're gonna start to get into the first kind of pieces of Rails where we're talking about how Rails handles requests.
Why is this taking so long to load? That's weird. Maybe it got stuck on something.
OK. So basically what it's done now is it's added a register button. Oh, button 2 instead of link 2.
I don't like that.
No, I want these to be links, not buttons, because that's going to allow me to better demonstrate this. Because what this is doing here is each of these is actually a form. And I actually don't think they need to be. It's much simpler just to have them as normal a hrefs.
So what I'm going to do is ask it to convert these, convert these buttons to link tos instead.
So Rails has these helpers. ButtonTo basically is just a helper that creates a button and then you can add icons and text inside, inside the block. It also has a helper for LinkTo as well, which is what we're going to use here instead of button.
So let's see.
What's nice here is that you can see it actually has reused the classes. So you can see I do get a nice button and it's nice and consistent. It's not trying to kind of build that from scratch with Tailwind classes. So yeah, and I should get it. Yeah, I get a loading state by default as well. Okay, so it looks like it's fixed that.
It's just wrapped. There we go.
So This is a pattern that we use as well, which I'll talk about in a moment, which is HTMX has this nice feature where you can basically specify the element that you want the loading content to load into. And so I'm actually going to show you what this looks like because it's very— it's too fast at the minute. So this is one of the problems with this is when I hit register, you'll notice there's like a flash here. So see that kind of gets larger and then because it's responding in 42 milliseconds. So it's actually like, that's a loading spinner, but you can't even see it because the response is coming back too fast. This is again why I get frustrated when people say Rails is slow. You can actually build very, very fast apps and experiences with basic Rails. So what we're gonna do now is just debug why this isn't working. So, okay, no route matches games/1/registeration.
Let's open up our routes file and see why that is.
Match games, game ID, toggle registration as via POST.
Aha, but it's a GET, so I don't think actually we need a POST here. I think this is fine.
Nice. Now that's cool. So that, that actually pretty much worked. We had to fix like something very minor, but that's pretty cool. So, and as you can see, like this looks like a React component, right? This is just HTML over the wire. All that's happening is I'm clicking it, it's getting the, it's getting the exact same page. In fact, it's probably doing a redirect, is it? Um, yeah, so toggle registration. Yeah, so, and again, these are patterns that I've prompted it with in the cursor rules file. So what's happening here is when I click on register, it's finding a registration for that user for that game, um, it's setting some toasts. Again, this is something that we've added ourselves to Rails, which is Rails has this flash feature, but we've kind of wired it up so it has these really nice toasts. And then it is either creating or deleting them and then it's redirecting. So you don't actually see the redirect here because all of this is happening in the background. So you don't have a page reload. If you look at the network requests, you'll see that there is two network requests. So the first one is to toggle, the second one is to show the games. And all of this kind of happens behind the scenes basically. Inside of a single view here, which is whatever, 29 lines of code. So that's basically, yeah, I mean, that's Rails with HTMX in a nutshell. Just to add a little bit of context, Rails doesn't normally ship with HTMX. Rails uses Turbo, a thing called Turbo. Turbo is great and behaves in a very similar way. So most of what I just showed you here is totally possible with Turbo. There's just a few UX details that Turbo doesn't do. And you can't control it with headers. And there's a few abstractions that are a little bit more complicated. So that's why we use HTMX, but you could get the exact same thing here with Turbo basically. That might be a good point. Sahil, just looking at the time to pass it over to you.
Awesome. Let's do it. Let's see how I'm doing. Share screen.
Boom.
Awesome.
So yeah, so I kind of my approach, it's a bit different because it starts kind of from the front end and goes into the back end, which I think is fun. So I've started using v0 a lot.
And so I think, you know, app, they literally just say pick up app, do find pickup. Games in different cities.
Games can have max players, location, end time, start time, etc. Be creative.
I don't know, something like this.
And so I can—
oh no, this is the old—
it's the old v0. Where's the new v0?
I don't want this.
I want v0 chat.
I don't know why they even promote that one anymore. 'Cause it's, well, we can see what this would've created. Maybe it's decent.
But generally I use the v0 chat because it's often not correct right away.
And I think chatting is kind of a better interface. So we'll let these things go.
And then I did this while you were streaming. So I kind of came up with this thing. Oh look, it kind of created weather for some reason.
For whatever reason, that's what it decided to do.
So yeah, this is kind of how I often kind of start thinking about stuff, because it kind of gives me, gives me kind of good ideas.
Like I kind of like how it has this sport players search kind of thing.
And then you can basically take the code if you want it.
And you can see it's kind of crashing my Safari. So yeah, they probably need to work on that.
But anyway, I did that. I took, took an example of it and just put it here and this is kind of what it came up with. But I do like that sidebar, the kind of like the header and I, I don't think we need to build all this, all this stuff in.
So I'm just gonna remove it and I'm gonna try to do everything in a single page cuz I think it'll be a lot, a lot more trivial. So I'm just gonna remove the header for now. Boom, it's gone. Go to page.ts.
So basically for the way Next works, and all I did was basically just clone this, this guy's Next starter kit.
So this Next Sass starter from Lee Robb from Vercel, they just published, comes with not too much, but basically kind of what you, what you saw with some auth and Stripe, if you wanna set up Stripe stuff.
So let me add this. So this is kind of how I use Cursor. Like, I kind of liked what it did here.
So is it— which part is it?
Yeah, this like search games, the sport dropdown players, kind of cool.
So I'm going to say, add a header navigation search bar that allows users to search for pickup. Names, a lot. And then cursor will kind of go through and you can kind of have it on the left. Like how you were doing it actually. So I'll do something similar, but ideally it kind of does it as we go. Oh, we broke something. So the reason is— I'll accept these changes so I can figure out what broke.
My guess is that I don't actually have this component in the thing.
But let's say, let's say I didn't know how to fix it. Let's say, let's see how good the AI is at telling me what's wrong.
Because one of my pitches for Next and just generally a lot more frontend stuff is that, you know, you can kind of get help from AI.
So let's see if this gets it right. Nope, it doesn't really.
It says to just do dot. For this, which is wrong, obviously, because these other ones are working fine. And the reason it's not working is because basically I haven't imported this in. And this is using ShadCN. ShadCN is basically just a copy-pastable thing where you can get these components. Makes it really easy to sort of not have to build all these things from scratch with all the different interactions. So this one, I guess, is a select, which I didn't have.
It was using for the search, which is basically this thing.
And the way you do it is you just copy this.
So we'll just copy this. We'll install it and hopefully that fixes the issue.
Yeah, cool.
So yeah, now it looks like this. This plus button doesn't do anything.
I assume nothing works, which is totally fine. Does this work? Filtering doesn't work? No. Awesome. So let's make the filtering work.
Search.
We don't really need search. Search is actually kind of confusing because what are you really searching for?
Let's just say, let's change search to a city location, like city or something like that. It's probably more reasonable. So we can do that and say remove search and replace with another dropdown for selecting the city. Also make it so that each dropdown updates the grid of games to match the filters. Also remove the search button because it's real time.
So yeah, the reason I kind of like doing things this way is basically I don't know what, you know, this often like Rails, you kind of start with the schema because it has this like MVC approach, right? So model view, or really model kind of controller view, but model view controller. And the issue sometimes I have with that is that I don't actually know really what the app should do. And so I often have to like remigrate a bunch of stuff getting there. So I kind of like fleshing things out with these kind of just like mock JSON basically. And then once I kind of know Okay, this is what I want.
Then we can like spin up the database stuff. And the rarest—
Wait a sec there, Sahil, sorry. Oh, okay. So you just gotta be—
okay.
You got your JSON at the top. How does that handle?
Yeah, so for now, so like if you—
So for now it doesn't probably, but we can, we can, we can sort of add it.
And so this is giving me some error.
Data.
What is this error? Can't resolve libdb. Where is libdb in my thing? So there's some, some issue.
And I find this is the most annoying thing about JS. It's like you just get a lot of these issues that are kind of nice because it means your site can't break.
You can't even deploy it.
Because it's all typed, but it sometimes makes it hard to develop because you can't actually move past without fixing these issues.
So let's just restart the server, make sure that it's not related to that. Okay, a select item must have a value prop, blah, blah, blah, blah. So let's fix this issue. The issue is— who knows what the issue is?
I don't know. I just know that these are the selects that are probably causing the issue.
So what I do is I just paste the error in and generally it does a pretty good job. So that's why I guess you can't have these empty strings in the select. All right, so that works almost. All sports doesn't work, but you can do basketball, soccer, kind of cool. So let's fix that bug real quick. And the bug is basically This filtering thing is probably not actually updating properly. So make it so that selecting all sports—
actually, you probably need more context. It probably needs to know how the selects are set up.
Make it such so that all sports shows all sports. Who'd have thought? That's what it should do. Same thing with any, same thing with all cities. And oh, it's, that's kind of funny, it kind of fixed the other ones for me too. So let's see if that works. Yep, so that works now. This works now. This works now. Cool.
So you can see all sports in New York, blah, blah, blah, blah, blah. Pretty cool, but you know, this is all static. Right?
This is not, you know, there's nothing here yet. So for example, if I click this, it's probably gonna break.
Pretty expected.
So let's try Composer and ask it to do this.
So when I click, and this is kind of how I work is like, I just pretend to be the user. I try to use it, it breaks, and I figure out what I need to do in order for it to not break anymore. So, and then I just kind of tell the AI.
That's again one of the things that having most of your behavior in a single file has over Rails where it's broken out into routes, views, models, controllers, is that you can, yeah, it's a little bit easier right now at least to a cursor to do it the way that you're doing it. Like if I, if I try those prompts, sometimes they'll work, but the fact that they need to change code in 4 different places and different languages makes it so that it's, it's not as, it's not as smooth certainly as the TypeScript React is right now.
All right, there you go.
It works.
Pretty cool. So let's just review kind of what it did, because ideally we want to not just trust the AI, but also trust and verify, as Hillary Clinton likes to say. So it created this route. So this is how— there's no routes.rb. Kind of file in Next, you just use the file system and when you have an ID like 1, or let's say I change this to 2, um, interesting that 1 works but not 2 works.
So we'll, we'll investigate that later.
Uh, but basically it, it uses this kind of like square bracket-y thing.
So if I increase the font size on this, you can kind of see this, uh, this thing right here.
That's kind of how it does its little magic. Lots of magic in Next, similar to Rails. And then you can kind of see it, it gets this, this mock data. So I guess what it did was it took out that mock data, it put it in a separate file, lib/mock_data. Oh, and you see this, this is why it broke, is it actually wiped the data. This is kind of an annoying thing that AI does is because it's limited on tokens, it'll often like get rid of your stuff, which is kind of annoying. And so in theory, what you could do instead of accepting it, you could say, KEEP_ALL_OF, and I often use all caps as a way of saying like, this is really important, keep all of the mock games.
And let's see if that helps, fixes the issue. And then we'll do maybe some UI for creating the game, creating games, and then we'll see if we can hook it up to Postgres and actually get it to be real data. So let's see if this works. Oh, it works. Awesome.
So now these routes are working. It's still just pushing from, pulling from mock data, but now you can see mock data has all of this. So pretty awesome. Let's accept the changes.
Close this.
Go back.
So you have this.
You can click these.
There's a, it's just kind of weird because there's no, there's no back button or like any, this kind of header thing that you might, you might want.
So let's, let's add that.
So the way that Next.js does it is it has these like layouts. These layouts are added automatically to the page.
So it kind of keeps the page more focused.
Kind of on the content. So you can kind of see that this, it didn't really do that.
It kept, it kept this layout in here instead of in, in here. So what I can do is basically delete this. Now it'll remove it from there and I'll just add it to here. See how this goes. There you go. And then maybe— all right, awesome.
So now we have this.
There's lots of dead code in here, but we can always clean that up later.
And now I want This other issue I have is I want the same partial header to be used here too.
So let's, uh, let's fix that.
And this is another good example of maybe where this would be helpful. Uh, so you want to make sure you have like the right pages open.
This was a tip I got from the Cursor folks.
So you have this guy, which is that this main page, you also have this layout. I actually haven't figured out how do you keep these open, but whatever.
I think if you focus this, I think if you click over and focus it, it should stay open. That's how VS Code works.
No?
Oh, like, oh yeah, yeah. Double-click and focus.
Yep, you're right.
Awesome.
Yeah, there you go.
Cool. So basically I want to just say what I was saying and then you're going to have to feed these in again.
Maybe, yeah, that should be all and just say, add the header with picked up games to the game ID page too.
And then it'll probably do something where it pulls that out, but let's see. One thing I, one feedback I had for Next is like, I feel like this layout should kind of be in all the children.
As well.
So if you have like game, but I get—
oh, I guess that's why, is it's technically in part of a different folder, so maybe it would have worked.
Uh, so what did it do? Oh, it was just lazy and added it manually.
Oh, whatever. That's actually maybe better for now because we can always dry it later, and then I can just fix this to be remove create new game here and instead have a back button on the left, which goes back to slash. All right, cool.
This is kind of weird that it put it here, and I think the reason is there's some justify-between stuff.
So I believe if I just added like a blank thing here, it would work. Yeah, something like that.
Cool.
That's good enough for now. Good enough for now. Okay, let's do, let's do, what else do we do?
Let's do, oh yeah, let's actually create this, do a create of this game.
Create a new game. So create a new game when you click the plus icon.
It's interesting that it didn't— it like uses Lucid React for some stuff, but then it puts in these like SVGs over here.
Do you—
I'm probably gonna delete—
have you given it guidance in your cursor rules? Like, have you told it? Cuz that's one thing that I find I had to really kind of get quite strict on is how do you make sure it's not mixing and matching libraries.
That's funny, it added a new thing.
Um, currently I actually don't have a cursor rules in this, so it kind of just goes willy-nilly every once in a while.
Gotcha. Uh, yeah, um, which is not ideal.
Um, actually, I kind of like how it, it did, uh put it there.
So I'm actually just gonna remove it from this layout, wherever that one is. Layouts.
This one. Yeah. Cool.
But this breaks, and I also think that like a new route for this is unnecessary and also just wanna show off the single, kind of single-paginess of it. So what I'm gonna delete these, these also like slow down. AI, because it has to go through all this code. Every line is quite expensive.
So I'm just going to delete this. It's going to break, break the stuff, but it's okay because I can just add it right here. And then I changed the name of it. So I'll just change this to shopping basket and it should fix.
Awesome.
Cool. There you go. And then I will Add a modal dialog thingy when clicking plus and ask for sport as a dropdown, number of players, city, start time, end time. I think that would be it. See how it does it.
Similarly, it may not have some of these things.
I might have to install them. Interesting that it doesn't actually do anything. But hopefully it makes more progress and shows us the UI at least. So yeah, I gotta do the same thing, go to the dialog over here, copy it at this.
And all this does is basically copy paste these files right here.
Pretty simple. So it's not really doing much more than that. But there you go. And select your sport, max players, LA, your start time, similar to, to you, you know, just using the browser defaults. Yeah.
And that's pretty good. But yeah, just to show you guys that, you know, this is kind of why I love ChatCN. It's like there's a lot of stuff happening here that I don't want to think about and I don't have to, which is really nice.
That's all ShadCN, is it?
What is it? All ShadCN.
Are they modals or what are they?
Figures, well, everything is different. They're basically just React components. Yeah. Everything, you know, they have an avatar, they have a button. The buttons give you a bunch of variants like destructive, default, ghost, link. So if you want a link that looks like, doesn't look like a button. Um, but it's, it's really dependent on each component is different. And what, what I love about it is that it literally is copy pasted. So. It's all your code, you know, if you want to change this to do something else, you can just do it. You won't get any updates, right? But it, for example, this clicking the background just closes it for me. And there's, there's some, there's some something in here that basically it imports this Radix UI React dialog component. So it's all based on, on Radix, Radix UI.
Is something that WorkOS built. Yeah.
And then ShadCN kind of built on top of it. So it kind of comes with all these, all these different—
They built it and were acquired by WorkOS.
I think that team is really—
Oh yes, correct.
You're right.
You're right. They're— yeah, exactly.
They're acquired by WorkOS.
Good branding on their part.
Yeah.
So awesome. Yeah. So that's pretty good.
It's not ideal that this is not full width, but whatever.
It's fine.
And so, yeah, so last thing I guess is let's actually create these games or let's actually, yeah, let's get this to create and then we'll make it use actual Postgres.
So that part is—
Do you use an adapter for Postgres? Are you using Supabase or something or are you—
So this, This one comes with this thing called Drizzle.
Gotcha.
Which I'm pretty new to actually. So we'll see if I can figure it out. Technically this little thing came with some stuff, so it shouldn't be too hard to get it to work. But yeah, basically this is like the adapter. And one of the cool things about it is that you don't have to generate any migrations yourself. So you can kind of just update the schema. And then in theory, it will just kind of generate the migrations you need on the fly. It can be wrong sometimes.
So there's a lot of— there's risk, but— Yeah.
It's kind of nice if you're moving really fast and you're like, okay, I want to add something to games that you can just kind of add it here.
And you know, you could say min players or something. And then it kind of knows. That's awesome. What to do. And so that's kind of nice. Yeah. So yeah, so let's join a game.
I think the simplest thing to do is basically, yeah, we should probably just, let's get this, see if I can get this actually talking to a database. That I think would be really good.
Yep.
Show people that.
So page, currently it's still getting this mock games. So I will again use Cursor and see how good it is at this and say, I'm gonna include that schema file.
And I think that might be everything. Maybe I'll include this one too, this other page.tsx that's in the game page and say, remove mock games and replace with actual data from POST.
So to see what it's doing, new import, there's now this get_games sort of internal API method.
And it's gonna break because it doesn't yet exist.
Also does the same thing over here.
It now fetch—
gets this libapi::get_game call, and then this is actually the new API. So you can kind of see the get_games does this like DB select from games. So similar to syntax like Mongo or something like that. Get game with an ID and then create game.
Which will create the game.
Nice.
Looks pretty, looks pretty good to me. So let's just accept it and fix it, because the chance of AI getting this right is almost very low. So let's see, what is this error? PostgreSQL. So I have no idea what this error is. Can't resolve TLS, can't resolve.
No idea, no idea what the issue is.
Where is the import coming from? The TLS, that line of code, TLS import.
So this is like, I guess, the Postgres package.
Oh, okay.
Sorry.
Which is—
yeah, it's not in your code base.
Yeah, exactly. It's not in my code base. It's some, some, some way I'm calling Basically, some, something that I've set up is incorrect.
So let's figure it out.
Libs.
So yeah, one issue I can already kind of tell, I think, is that this doesn't match.
If you notice, like lib, there's no lib API. It's—
oh no, there is. No, yeah. Lib API. So this is—
this feels like wrong to me.
It feels like it should be in this API, but whatever, let's figure it out. Why is this happening?
It's funny because this is like all the stuff that trips you up with, uh, with, like, I feel like generally this shipping is fast, but then you get stuck on these random-ass bugs.
Yeah.
Mostly because this stuff is not built for building web apps. It's kind of been hacked, hacked together.
But yeah, if I look up for this, probably won't even show up in my codebase. So I literally have to figure out why this is happening with almost no context.
Which is why I'm assuming— I'm assuming YFS means file system. So I'm assuming it's some— that package is trying to interact with your file system and for whatever reason it can't, or whatever reason it doesn't.
Yeah, it might be that.
Yeah, I'm gonna see if I go through—
maybe I just didn't complete his, uh— oh yeah, maybe I need to run this migrate thing because of the changes or something. Oh nice, I got, I got a new error, which is good. I guess there's some Stripe. I wanted to rip out Stripe because I don't want Stripe all in here, but there's a bunch of Stripe stuff in this thing that I—
this starter kit. So my guess is that's also probably causing some issues. So I might as well see if I can just— yeah, like all this stuff I can probably just remove.
So this is what, you know, remove Stripe stuff.
This is like really good for stuff like this. Ooh, just delete all the Stripe code. Run this migration, run seed. Oh, the seed almost definitely remove all Stripe code.
Now this should work.
Nope, it didn't work. Why? Oh, it's because it already created the user, so I'd probably have to do some sort of reset.
I think this is a good example of the, like, the trade-offs of each. I think the Rails approach, there's less complexity in the Rails ecosystem and just in terms of levels of depth. Like, it's very difficult to go that level deep. The JavaScript ecosystem obviously has packages built on packages on packages, so it can be more tough to sort things out. But as you said, like, once you actually— I'm guessing once you get past these things, then it's, it's probably a little bit smoother, you know, than what I'm—
Yeah, exactly.
Like, if you're working on an existing project where all this stuff has already kind of been figured out by somebody else who's smarter than you, it's, it's not too bad.
But when you're starting from scratch, it can be, it can be kind of rough. Well, the easiest fix would be just to change this email and then run the seed again and see if it passes. Uh-huh.
Awesome.
Well, that was good. We got the seed. And in theory, seems like that's the only thing I really needed. So at least that bug is— oh, there's one other thing. This export doesn't exist anymore because I took it out because, oh yeah, there's of course no Stripe anymore. I can just delete Stripe. Start from here and then delete start from here. And this is actually one of the things I love about it is it's just very easy to know basically what, what to delete because it's all typed, which is kind of nice. It makes it much faster to kind of debug certain, certain things. This is also actually payments. I also probably don't need anything related to payments. Let's get rid of that. This is also unnecessary. I got all these errors. Uh, what is this? Update team subscription is gone. Fuck. Why, why are you giving me this error?
You want to switch and I can take over and you can debug?
Yeah, you want to switch?
Yeah, that sounds good.
I don't mind.
Yeah, it's up to you.
It's good. 30 minutes or so. So yeah, let's do it. I'll unshare my screen and you can, and I'll see if I can figure this out pretty quickly.
We'll see.
Yeah, I might need to ask the same favor as well. Like when, if I get stuck.
No worries.
Excuse me.
Okay. I actually— I haven't really done anything while you were doing that because I was interested in seeing what you were doing. So, I think actually one good analogy here, you just did a modal, right? That was like something that you did with Radix and ShadCN. And so, I can kind of just show like what modal would look like in what we do the way that we've kind of specced out the codebase. So, what I'll do is I'm going to add an edit button. Oh, actually, one other thing I wanted to say. I love the v0 approach. And I think actually when you compare what I did, where I started out with the data model and what you did, you kind of get to something useful much quicker. So I haven't been using v0 that much, but I'm definitely going to start to use it. I also, for some reason, thought it was only TypeScript that it exports, but it looks like it will give you HTML. So I'm definitely going to start using this a lot more because I think this is something that's going to speed things up quite a bit and make them look nicer, faster. But let's jump back here. And yeah, I think I'll just start by adding an edit button. So ideally, you see an edit button if it's your own event. So if you have created the game, then you see an edit button. And then when you click the edit button, it'll open a modal and you can modify the details of the event. So let's, let's do that. So you know what, let's start with, let's start by just doing it without any permissions on it. I'll add the permissions later. So Um, let's go back over here. It's tough to get your brain back into the swing of coding again after this. Okay, um, so I want to add an edit button on This is Games index.
Okay, so, uh, add an edit button to the card on the top right. Uh, also add The route that handles the route controller and view.
Sahil Lavingia. Oh, hang on.
I didn't do that in the composer, right? So it's not going to add the routes controller and view. Yeah, I did this in the file.
Okay.
Well, let's just see what we got over here. Yeah, that's because it didn't.
So I'll go down to the composer.
Add a route controller and view for edit game form. A. So it's gonna create a route, create, modify the controller.
I'm gonna YOLO this again. To use this form.
Yeah, okay, I know that.
Nice.
And that should enable that. So this form should work now.
Great.
So let's open it up. Yeah, okay, got you.
So again, it's trying to use attributes that don't actually exist. There is no attribute called shortcode, although that is in my cursor rules file because I use shortcode often. So that's why it's gotten confused there, to be fair.
Let's just find shortcode here and see. Yeah, so Actually, let me open up the RIS file. Yeah, we just use ID instead of shortcode.
Sorry, where's the edit?
Edit game path, game ID.
Again, every time we get stuck here, it's just because it's using attributes that don't exist. So if the cursor guys are watching this, if you could figure out how in Rails codebases to not— to solve that problem, because it's probably 90% of the pain, really.
So let's find start time. It's not called start time, it's called start. Start.
End is not called end time, it's called end.
Okay, nice.
Is it using—
yeah, I don't want to use floating inputs. So although let's just see, because it could be nice. I think floating inputs are broken because of LastPass. No, they're not. They're just generally not working.
OK. Don't use floating inputs.
Again, that floating input class is taken from a CSS file that I've already loaded in and told it about.
So, okay, here we go. Um, description.
So we said that we wanted this to be a rich text field. So I use a thing called Rhino Editor. Which is actually built on TipTap. Again, like one of these things is that the JavaScript ecosystem has all the fancy toys. TipTap is probably the best rich text editor out there, but actually there is a Rails compatible kind of adapter for it, which is what I use. So let's just see if we can get that working here on the edit page.
So where's description? Yeah, so I think I can just say like use Rhino editor here.
Look at—
there's a file where I actually have this referenced in this codebase. What is it? H1 Rails. I think it's views, H1 Rails, demos. Yeah, demos/index. Why not editor section? So gives it enough knowledge.
Yeah, sweet.
Oh, why did everything else disappear? What happened there?
Uh, oh. That's very odd. There we go.
Okay.
It's just a syntax thing.
Okay. So now we have, again, like this is kind of super nice. And this actually should have persisted to the database. So you can do formatting and indentation and bold and links. And because this is TipTap, you can then use the plugin ecosystem for TipTap as well.
So all the docs are on Rhino.
These inputs are just normal Again, using the platform. So these are normal daytime pickers. You can probably use a fancier thing here if you want to.
And then we've got a location.
So trying to think, oh, you know what else we could do on the location side? I was working on this recently.
So let's see if we can use a fancy location picker. So I got this wired up recently where it's connected to, actually connected to Radar.com, which it should do search or location autocomplete.
So let's give it a few minutes and see if I can get this working.
Um, use the same location picker component here from, again, from rails, uh, demos/index.
I think I'm going to have to wire this up with an environment variable. Yeah, OK. So all it does is just render this component. And again, people who are used to component— one of the biggest shortcomings that people kind of have with Rails is that it doesn't do components. So I'm trying as much as possible in this demo to just keep it as pure Rails as possible. So this comes out of the box at Rails. You have a file and you can render that file. Normally you'd actually do render partial.
Not really generally advised.
But you do, yeah, you do render partial, that's the syntax. But this is kind of, this is just including a file, obviously passing some variables through to that file. There are other kind of libraries that Rails has. There's one called Flex, which has become quite popular where you can build your own components, which is much more kind of familiar to the JavaScript ecosystem. But for here, again, I'm just going to use the basic, just including a partial that's already in the code base. So I'm just going to look at that partial now and see what it requires. So yeah, Radar publishable key. Okay.
I'm going to switch over to my other window here because I don't really want to expose any sensitive data.
So I'm just going to set my publishable key over here.
Pickup app config application. Radar Publishable Key, where do I have that? I was just doing this recently.
Here we go.
So let's see, I'm gonna have to restart the server here. Just gonna exit the Composer and restart the server. Let's see if that loads in what we want it to. Oh, look at this.
No.
Oh, very nice. Awesome.
Okay, now I don't actually have it. So what's happening there when I've selected it is, is selecting the data in the front end, but it's not actually showing any feedback. So I'm actually going to call that location data.
So field name, game location data, because it's not going to save a location ID. OK.
So one last thing that I want to do is— actually, this edit screen is much better, would work much better as a modal, because right now it's standalone form. So we have this nice pattern where we can basically in a single line in the controller We can do, we can use a before action, which is render as modal.
And we wanted to only do this on edit.
Yeah, so let's see if this works.
Nope, getting some issues here.
What's the issue?
Undefined method render as modal.
Okay, that's fair enough. What's the actual method called? Renders in modal. Nice.
Okay. So, that's kind of similar to what you were talking about there with, like, ShadCN and how you do modals and stuff. Again, I think whatever library you're using, you're going to have to obviously figure out, like, what's the best— how you do certain patterns. And so, we've got this little library called base styles.css that basically handles modals.
Although it does look like there's an issue here with the modal. I'm going to brush over that for now.
So let's just make sure this form actually works.
Monday evening pickup. Update game. Did that work?
OK, it looks like it probably saved.
Let's have a look at the network. Yep, so it edited and— oh, interesting, but it's not closing the modal. Why is it not closing the modal?
So this is where it's tougher for— it's going to be tougher for AI to figure out why that's why that modal isn't closing, because it's our own implementation that we have here of the modal. So basically, I want to make sure this is actually loading. This is a thing called Mini.js, which we use, which just allows you to set variables, global variables, and then have your UI respond to them.
So let's see. No. If params got model. I wonder why that is.
Okay.
And I don't really want to get stuck for too long on these things.
What's the next set of features that we can build here? So we have manage your own games, register for a game.
I want to make this look a bit nicer, actually. Because this looks kind of crappy right now and yours looks way nicer.
So let me just go to games index and see what I can do here. So firstly, I'm going to say use title pair, UI title pair for the title. But the edit icon, edit button. On the right, add icons from Heroicons. So add icons for date and location. Oh man, did it change some of these attributes again?
No.
Okay, very nice.
That looks quite nice, I think. Register, deregister.
Let's do some filters as well.
So you had filters on yours. Just trying to think of what would make sense. Yeah, let's add sports and do filters based on sports. Also, I want that edit icon to be a little bit nicer.
Um, UI button, let's remove minimal. Don't want it to be minimal. There we go.
Okay.
Um, it would be cool to have an image here as well, wouldn't it?
So where could I put an image of the actual location? So I'm going to say insert a top section here with the location image as the background image 79.
I think it's background color.
Oh, it's doing some absolute positioning here.
No, so it tried to use loc asset path location image.
That's not what I'm looking for at all. Um, you want to use the Game.location.image. Undefined method. Oh, that's dumb.
Okay, so I just put it in the total wrong place because it needs to be inside of the each loop for the game. So I mean, let's just see if I can copy and paste the whole sale in here and see what happens.
It's not called app game, it's just called game. What's the name? Again, so much of these bugs are just because Cursor is not intelligently inferring the names of these attributes. So we got model game. Oh, sorry. It's model location. Wait, didn't we add an attachment for location, or was that on the game?
What?
What is going on? I wonder at some point, did Cursor remove the has one attached image? That is bizarre.
Have you ever seen that happen?
It does sometimes do that. It's really annoying.
It'll just delete.
It'll like, yeah, it needs to be a cursor rule. Like, do not delete any existing code. Yeah, it often will do that.
Um, okay, so it didn't actually do what I wanted to do here, but let's— is that for every game?
Okay, you know what I'm going to do?
I'm going to create a few more games here.
Um, and let's create a sports model too.
Do this as a separate model, I will.
So just to allow us to create different games in different sports.
So we go Rails g model Sport, name, string.
Yeah, we can add metadata later, like indoor, outdoor, those kind of things.
But for now, we just keep it simple. Rails db migrate, Rails g active. Admin, resource, sport. Okay, and then every game is going to have a sport as well, and I think that should actually be enforced. So belongs to sport. Nope. Um, and then sport as many games. I'm gonna need to create a new attribute, uh, just for— okay, so technically now can I create a new sport in here? I need to permit the parameters.
Soccer.
I apologize to the European viewers for calling it soccer.
I'm in America at the moment.
So, um, and then the game, we're gonna add the input here for the sport.
Oops.
Yeah, that's so annoying. It just autocompleted these as datetimes, which I don't want. Okay, so this is a new game. Let's just edit the existing one with the correct sport. Oh wow, that is funky. Uh, I need to allow it to accept sport ID.
Oops, soccer.
Great.
Okay, let's create another one. Um, so Tuesday, paddle tennis.
Um, oh man. This is absolutely painful.
Let me know when you're ready to switch off.
I finally got this set working, which is really nice. Thank God.
I don't really know what I did, but Oh, that looks awesome.
Yeah. So I mean, the title obviously doesn't make any sense.
Game locations.
Game locations. But that could actually say the name, couldn't it?
So if we just put the name in here, game— no, just game.name.
And then this could actually be game_description. So Rails gives you these really nice view helpers that you can just very quickly do inline, like truncate. So you can basically say just show the first 100 characters. That's probably not going to work because that is HTML. So that's actually going to break.
Can I— what's a good way to convert HTML to just text content? Let's just see.
Yeah, okay, that doesn't work.
Well, let's not truncate it for now and just actually render it.
Okay, so that's just going to show the description basically. You got your edit button there where you can edit your game.
Then you can register for a game and deregister as well. Yeah, okay, it's a nice little bit of progress.
Yeah, you want to take over?
Yeah, sounds good.
Awesome. Cool.
So I just broke something again as I was working on it. Uh, but yeah, at least this part works where I'm now actually, uh, hitting the Postgres database to get this data. And basically I think there's just like some old dead code, so I just try to delete as much stuff as possible. I also, this is something that I'm still learning with Next, this might not actually work because I don't know if I'm on the right Next version. But basically the issue, one of the issues I was having was I didn't have a useServer at the top. And so on certain places where you have query, I needed to add this useServer.
At the top, otherwise it basically gives you an error similar to like those frozen strings in, in Rails land.
And so I did that.
This works.
Before I go to the, the thing I'm gonna work on next is this kind of page joining and game and just sort of the relations between players and games.
But just to show folks that this works, you can say soccer, say maximum players 1. SF, 12/12/2024. We can always make this better later.
City and location is a bit redundant, but I guess not.
I can, we can say whatever, Crissy Field or something like that.
By the way, highly recommend going for a walk over there.
Beautiful.
So kind of cool that this is something I decided to do. This would automatically redirect to the game. Uh, and so now you can see your games.
All the other stuff should still work just as before unless AI decided to break it.
And then I wanted to add basically the ability to join a game, leave a game, and then ideally not join a game that, uh, that, you know, has too many, too many players already in it. But the cool thing is, at least now I think that like the DB stuff is kind of working.
It should be pretty fast.
Kind of iterate, iterate on the schema and add, add more functionality.
So if I join a game, I can click here, I can join the game. It breaks. Awesome. But at least we made some progress with showing this. I also wanna make sure that you can join the game if, if there's too many games already happening.
So yeah, so this is one nice thing about, yeah, basically typesetting is type checking is you can kind of you kind of know what the issue is.
When I hit this button, it's doing this object—
oh, this weird— you can kind of see it's some JavaScript annoyance, but adding 1 to an object. And so kind of that's what's happening here where this games.currentPlayers is returning an object.
And so let's figure out why that is. My guess is it is— I mean, it did— AI did suggest me something actually. But let's see if I can get AI to just tell me what the issue is. Yeah, so basically you have to do this wrapping, wrapping around. So let's apply it to see the difference. Let's see if that actually would work. Oh, we also probably want to do it over here. And my hope is that that works. Cool, there you go. So it shows this game is full.
You can leave the game, it changes that.
So that kind of works pretty nicely. Uh, this join game thing, I guess, is kind of redundant.
It doesn't add much, so we could even inline it if we wanted to. Um, but I think the biggest thing that I probably should add to some degree is some basic authentication and logging in, logging out maybe. I deleted that really fast just to get it to work, but at least now we can add it back and people can see how that kind of works in this, in this world.
And we're using Drizzle, so I went to the Drizzle docs to make sure I was using it correctly.
So yeah, it's similar if you're familiar with Rails, you know, it's the same thing, DB, you know, Rails generate migration, migrate. So it's not, it's, I'm sure it's inspired directly by, by Rails because it really was super innovative on that.
So, so yeah, so let's go to the schema, schema.ts.
One of the really nice things about it is you can basically have all your types are kind of inferred from the schema. So you don't have to like re-implement. A lot of your validations, they basically just don't even really exist in for sort of a fully TypeScript land. So it just, you just have this export type user typeof. And so this still has users, but there's no passwords.
There's no, there's nothing else in here.
So maybe we can do locations. I think that might be a good one to do.
So we can just say add locations as a new table. Because there's this inference here.
And you can see now that it does this.
This is kind of how it does it in—
That's pretty cool.
So it's pretty, yeah, it's pretty nice. And because it's all in one place, you know, you don't have to like specify a bunch of files. And then this will break because this location ID doesn't actually work.
You have to actually run those migrations. So similar, similar to Rails.
So, you know, pnpm drizzle-generate.
You can kind of see how it does this.
Is location ID column in games created or renamed from another? It's created. It's a new, new column.
And so it does that.
And then you run those migrations.
And there's some error here.
Contains null values.
So.
It's required to be not null.
And I guess the— that's weird. Why is it not null? I'm not doing anything. Location ID, this not null thing. Well, the easiest way to do it is you can say you're allowed to be null, right?
And then same thing, just generate those things again.
Migrate.
Oh, still happening. Let's figure out what AI says about this. Allow null values.
Oh yeah, .nullable.
I can't just do that. I have to actually add this nullable bit.
Oh, but it doesn't like it.
Nullable type does not exist.
So maybe this is like a Drizzle thing that it doesn't know about.
Oh. Nullable.
Well, I would assume not null because—
oh, no.
Well, let's just look up what Drizzle docs say. Where would the nullableness be?
Look at all these people with this problem.
This is what people hate about, about JavaScript land, TypeScript land.
Yeah, well, that's definitely, I mean, Yeah, Rails has been around for so long that I'm sure these were all things in the early days, you know, and then they were just kind of nicely abstracted. And so it's at the point now where you just, you don't really need to deal with a lot of this.
Exactly.
And yeah, and this stuff is so new that it's like there's not a lot of training data on it explicitly. Like, it's like, okay, here's one doc in which there's an example of doing, you know, stuff, stuff like that.
But let's see where that one-to-many is. Or basically what I'm trying to do. Oh, that's so weird. Well, let's just go back and just force it for now. I just keep it this way so I can keep building stuff. And let's see if I can get the migration to pass. Oh yeah, and people can see these, these are the, what they, what they look like.
So I mean, in theory it should have worked because all you're doing is dropping the not null or adding it.
But there's some, there's some issue. Oh no, sorry, not this one. This drop set, it should work. It's pretty simple, but yeah, but alas, it did not.
Why does it say null values?
That's what I don't understand.
It's not—
is it that the there's already data in the games table that has null values in it that you need to delete those rows in that table. Do you have like a console where you can directly access the database and drop those things?
Um, let's see, actually, I don't know. I've never done that before.
Um, but presumably, yeah, you should, right? You would think that there'd be a way to do that, or just like drop and start again, you know?
Yeah.
'Cause really, I don't need any of this data.
There are all these different— I'm still very new to trying all this stuff out. Studio sounds like it to me.
You have to install that, do you? It's not like a hosted thing. Does Drizzle not have like a hosted UI on top of its Drizzle?
It has this sort of thing. So yeah, I guess it does.
It's just connecting to and then I have to install it. Install it the right way, I guess, which I don't have.
It should. I mean, in theory, it will connect to your database. But I probably just need to specify it. It's probably not connected to the right one.
But what I could do, because this is, I don't really care too much, I will expose my env and we can just see if it works with this. But one of the, yeah, one of the nice, let's see if that, so what did it say? It said Drizzle Studio. Where is the Drizzle Studio one? Drizzle config. So Drizzle config's right here.
And then I have to add, oh no, see, it should be able to pull it. My guess is, oh, I'm using Neon.
So probably I just need to go, let me do it in a different window so I don't reveal anything, but it's probably just in Vercel somewhere, so I can go find it. So go to my projects, see if it's here, and then maybe I can just open it up directly from Yeah, okay, cool. So I, well, the issue is there's no location ID on these games. It doesn't seem like that worked.
But just to show people, the pros and cons of this is basically this is like embedded into Vercel. Yeah. And so you can see basically it's like super vendor lock-in, but you can see all the games right here. So the cool thing is this is actually completely live data. This is not actually a local Postgres. It's actually hitting production. So actually it's, you know, if I'm ready to deploy, I just have to deploy the front end and you're basically done.
It's already using the right, the right stuff. So yeah, so Basically, I'm doing something stupid with this. And so I'm just going to keep trying stuff and seeing if I can get it to work. And if it doesn't add anything, then the issue is not actually here.
Where did that error come from?
Location?
Maybe I should—
it might be this issue because the C data—
oh, this looks like it's doing something.
I think the seed file I need to update.
Okay.
We'll try, we'll try this and see if it's better. And then why is this not working? Who knows? I'm not gonna read all that. Oh yeah, same kind of issue, I think, which is It needs those things. This is going to have another issue. This is the beauty of typing for you.
See it.
Please work.
Nope. All right.
I'm failing hardcore.
Anyway, I don't know if I'll be able to get this to work.
Let's go back to what it was before. It was so clean before.
Yeah.
Damn it.
I was literally, it was working, wasn't it? I got too fancy with my migration.
I mean, it makes sense to have a location, so I don't want to remove that.
How do I—
what's the error right now?
The error right now, one second, the error is probably the same thing that was before, which is, yeah, this location_id doesn't exist.
Okay, and if you look at the actual table on Vercel, um, I think it looks like it's just a text field.
No, is there—
is no location ID.
Scroll over to the right. It's not a relationship.
Exactly. Yeah.
It doesn't, it's just an underscore. Unlike, so basically the migration is not running.
Yeah.
So it's not even getting, it's not getting that far.
Gotcha.
So you're trying to add that underscore basically in a migration.
Exactly.
Yeah.
I'm trying to add this new column.
Yeah.
I'm wanting to remove the old column.
Basically.
Yeah. And ideally, what should happen in the context of Drizzle is when you do generate, it should actually create it. So for example, like it should delete— there should be a migration that's created, which is here. So you can kind of see it here, create table.
Yeah.
And then it should effectively drop this and my, my guess is what might be happening is basically there's just the way I set it up that, that migrations are running in a sequence and breaking because it's this not null is created, which fails because there's, there's nothing here.
So I basically need to like start kind of from scratch, which I've never, I don't know how to do properly, but there should be just like a way to, to reset. To reset, you would, you would, you would think.
But, but I mean, the simple—
the, the way to fix this really, really quickly would just be to revert this back to what it was, and then at least that way you— oh, still wrong. What's going on? Yeah, I'm getting into a really bad state.
You want to switch again?
Uh, let me, let me keep going for a bit just to show people how annoying this stuff is.
Yeah. And we'll see if I can get it to get out of my I, I mess up. So Drizzle has— how do I reset? Basically what I want to do is reset everything, and you would think that there'd be some sort of way to reset, but it might be that I Just may— oh, maybe I just need to delete these. One thing that might fix the issue is if I go back to having that location. Is it ID or is it like this? Make location ID reference location.
Might be that I think I might just need to delete those files.
I think that might be the issue. So let's get this to fix. That doesn't seem right to me personally, so I'm going to change that back. Oh, fields. Oh no, it's a difference. Not what I meant, but So I'm just going to delete these files and see if that works. Because really all these kind of got messed up right around here. See if this will correct anything. Oh, it wants some. All right, sure. That's what it says.
Awesome.
And then we're gonna see if this works. It'll generate. It doesn't think that there's any new schema files, so it's not gonna do anything, which is a bit scary to me. I feel like it should have done something. No. No file. Oh, I deleted. Maybe deleting them was not the right move.
How does it know?
What is the— how does it know? In Rails, there's actually a table called migrations which goes alongside the files themselves. So I don't know if Drizzle has the same thing where there's a migrations table that's not exposed to you inside Vercel that actually has timestamped information.
I don't— it might, but I actually think it doesn't. I think this is what it uses.
Like it just creates these files and commits them.
But yeah, basically it looks like this journal is being used.
So maybe I just need to remove these up to here. Funny Angel is the last one. Okay, it said it was applied successfully, whatever.
Still an error.
Uh, still the same thing. Location does not exist. Oh, it's going back to what it was before. Oh, well, at least that's back.
Nice.
So that's good. I mean, it kind of works, but I still think it would be cool to do a bit more, like, but I mean, functionally it's there.
I wanted to, I really did want to add users because I think it would be really cool to be able to kind of log in and log out.
Yeah. But it might not be possible as part of this, and we're already at 5, so.
Yeah, maybe we can just wrap it up. I think it was a good overview of the pros and cons. You wanna add? So yeah, I guess I gave a little demo of my little thing. All this kind of works. I think the biggest, you know, the definitely I feel like the win really with having everything in this language, obviously the backend is much weaker, the frontend is much stronger and kind of vice versa. Yeah.
And, and so yeah, we'll see if we actually complete migrating Gumroad to Rails or if we end up just like keeping it in Rails and just moving, moving more and more to React of the frontend.
But I think the big, the biggest stuff that I would focus on is probably just making this form a lot better. I think there's a lot, like this is kind of janky. I do think ShadCN has some date picker things. I'd probably.
Yeah.
End up using that.
You could do some, some nice magic around, you could type tomorrow and have a, using Vercel AI SDK, stuff like that is where I would spend most of, most of my time. But at least this stuff is, is now, is now working. But you know, stuff like this I wanted to do because you, you shouldn't be able to join multiple times as the same person, stuff like that.
But Such is life. Awesome.
Yeah, it's nice to be able to see like end to end what it looks like. And I think I agree with you, there's definitely pros and cons to each. I actually think like my biggest learning is I think having your go-to library, like your ways that you do things and just having like we've got a bunch of documentation on it. I think that's the thing that prevents all of these like kind of what's it like, quicksand, you know, like getting stuck in the mud is, is knowing like how to do specific things. I actually think like as AI like continues to kind of grow, I think that the role of the CTO should become more and more that basically. Like right now CTOs are kind of like masters of like people, right? Like they're just managers effectively and then they manage people, infrastructure, and they don't really manage the code that much, you know, like they let the people manage the code. Whereas with this stuff, I think actually you need a lot more organization, like whether it's frontend or backend, whether it's Next.js or Rails, you do need people to be able to say, this is how we do this thing, here are the standards, here are the patterns, and you have the kind of Bible or the playbook now. So you guys can go and do it and you should be able to do it very fast. So we should need less, fewer people and have more consistency. But I don't think we're at the stage yet where you can outsource that to the AI, even if you're in Next, Next land, I think you still have to have some—
100%.
Yeah.
Some guardrails on it.
I think the biggest thing is, yeah, I think the, is to basically have a, a thing that you can clone that gives you all of the things you need. So you're not spending your time getting stuck in the mud on basic stuff like user auth, signup, you know, things like that. You can kind of focus on just building your app. Uh, yeah. Like for example, like I tend to use Clerk. And Clerk is like a little nice way, but I noticed actually when I, when you were working and I was trying to set up some stuff and fix some stuff, I tried to add that and it, there's no Clerk in this repo, but it just added it anyway. So like, that's how, that's how biased the AI is in, in terms of using these languages is there's no reference to Clerk.js, but somehow it decided the right way to solve this problem is to wrap everything in a Clerk provider and yeah, and do that.
Yeah, it certainly seems like there's more training data in React land as well and in JS land for sure.
Yeah.
What do you think? Do you still hold the view that Rails is tech debt?
I mean, I think not. No, I don't think so. I don't think it's— as an absolute statement, definitely not. Which is how it was presented. Which is kind of how I feel like Twitter is often truth by omission. You know, it's like technically true, but you're not saying the much 99% of what you really, the context. And actually Jacob from Beehive came over to the office and he was like, look, if you really think Rails is bad, just switch to Go for like the highest performance stuff. So yeah, I think a lot of this stuff is convincing me that like, I don't think we'll probably move completely to TypeScript, at least not for a long time. And certainly the, the ecosystem, I think it is I think it, it may become so, I guess, like the tweet is more such that I could see, you know, if, if technical debt is sort of termed as may need to be rewritten eventually. Yeah. I think it's sort of like 10 years from now, once the TypeScript ecosystem is much more developed, I think it's more like I can sort of see where the world is going. Yeah. Like types are really nice. It would be nice if Ruby had them. You know, certainly the ActiveRecord and DB stuff, it would be nice if TypeScript had those kinds of things in their ecosystem. Yeah. ShadCN is really nice. It'd be cool if it, but both of these things are happening.
Yeah.
So yeah, I would say it's not, I would retract the statement.
Nice.
I'll do that soon.
Awesome.
Also, I just need to get better at TypeScript, I think.
So that's what I need to work on, improve.
One comment on that though, as well, in terms of playing out the longer term and maybe in 10 years Rails is considered legacy or tech debt. I do think there's a flip side to that too, which is that I think on a long enough time horizon, I said this to you in the office, but I think everything will tend towards the platform. And so these obfuscation layers, these additional layers on top that are not actually necessary will also be considered tech debt. And eventually everything will get subsumed into the platform. I think there's many things that have to happen before that can happen. I don't think web components, for example, are good enough today for you to be able to do all of these things that— like, the thing about Next and React is the composability is just so good. The fact that basically every single piece of data gets passed through this kind of chain of ancestors. You can't really break that chain. And then you can couple styling with interactivity. It's just the composability right now of that ecosystem is, I think, I think that's the thing. That's the strongest thing by far.
And when you—
even when you look at Rails, like all of these components, everyone's building new component libraries, but they are not part— they're not interoperable with— like the Stimulus component libraries are not interoperable with the Laravel component libraries. And that's what you miss. And so I would like to see more people solving that problem, you know. And I actually think the way to solve that problem is figuring out other layers of composability. So it could be web components or it could be like lightweight, like things like Alpine, like these sprinkles libraries. I think a lot of these components, like if you actually go down through those lists, particularly ChatCien, like I would say 90% of those components consist, like the actual value is add a class to this div when this div gets clicked. Like accordions, for example, or modals or tooltips or those types of things. It's like add a class. All of the heavy lifting can be done in CSS. CSS has animations now. The modal you saw earlier on was all CSS. So it's like, I think, yeah, finding better ways to do that and to share the kind of the value that we are, or like the, the libraries that we're building across different platforms and rather than building everything from scratch each time, I think, yeah, longer term.
Um, yeah, 100%.
Just depends time horizon.
Even both of us use, use, you know, take advantage of something like a Tailwind, right? Like we, we clearly both value something like that and we're not writing all of our CSS from scratch every time and Ideally, these things get embedded into web standards and so, yeah, everyone gets them for free.
Yeah, exactly.
Yeah.
Good stuff.
That was fun.
That was fun. Should I demo mine before we finish?
Yeah, demo. Let's, yeah, let's demo yours and then maybe on Twitter we can ask for a, we can ask for a poll.
I should have, I should have done more work while you were coding. I should have like fixed it up a little bit. I mean, this is what I ended up with, was a list of—
let me zoom in a bit—
list of events. Oh, also there's the backend, which I think is worth quickly showing as well. So you've got all your data models and you can go ahead and create new sports and new locations and add registrations. Then on the frontend, we have a list, we have the ability to edit the list, edit the events.
We've got some rich text going on in here.
Oh, nice.
Awesome.
Yeah.
And then we have—
oh, description is a really good one. I should have thought of that.
I should have stolen that one.
Um, and then we have these like nice animations as well, like these states where you can close a modal and you get your, your toasts and those kind of things. And then you got your register button as well. And again, people think of Rails as this like slow clunky thing that when you click on buttons you have to wait for everything to load and I just, again, this is more actually, this is less of a Rails thing and more of a platform thing. The platform now is at the point, things like HTMX are getting really good at morphing. So basically you can load in all the content for the next page. That's exactly what's happening here. When I click this, literally the entire next page is being loaded, but the frontend is smart enough to only take that little div, the little one part of the div that needs to be changed, which is this. And it can all happen in 20 milliseconds. So this again is what I mean by a lot of this getting this stuff for free versus trying to granularly target these divs. You can just build your app as links and forms and still have it be really smooth and really nice without needing to get deep into complexity. That's what I built.
Awesome.
Very cool. That's awesome. I think we both had— you can see the differences. I think your front end actually might even be better than mine. Um, so yeah, there's a lot of— there's a lot of fun. Thanks for doing it. Thanks for the being good-hearted about all this stuff.
And, uh, yeah, yeah, we'll see what—
we'll see what the—
what the people say.
Awesome.
All right, thanks so much.
Take it easy.
Bye.










