Rooftop Ruby Podcast

31: An Accordion of Complexity

November 07, 2023 Collin Donnell, Joel Drapper Episode 31
31: An Accordion of Complexity
Rooftop Ruby Podcast
More Info
Rooftop Ruby Podcast
31: An Accordion of Complexity
Nov 07, 2023 Episode 31
Collin Donnell, Joel Drapper

Collin and Joel discuss creating the router and ORM for Yipee! (Joel's new web framework) and how to think about using resourceful versus custom routes.

Follow us on Mastodon:

Show art created by JD Davis.

Show Notes Transcript Chapter Markers

Collin and Joel discuss creating the router and ORM for Yipee! (Joel's new web framework) and how to think about using resourceful versus custom routes.

Follow us on Mastodon:

Show art created by JD Davis.

Collin: Hey Joel, how is it, uh, how, how is it going over in England today?

Joel: It's pretty good.

Collin: It's starting to get real rainy here, so I'd say we're, we're in the part of the year now where, uh, we're getting the weather that Portland is known for. I don't think you're going to hear it in the background, but it is, it is quite rainy and windy today. 

Joel: It's kind of similar here.

I don't know if you can hear the, the fire in the background.

Collin: the fire?

Joel: It's, it's cold enough we've got the, the log burner going now.

Collin: Oh, okay, that's, that's much less troubling than what I thought of in the beginning. Um. That makes sense. I don't have a fireplace, uh, but I just have central heat, so it's kind of boring. On the other hand, I don't have to maintain a fireplace, so I guess it's, it's a trade off.

Joel: We have central heat too, but

Collin: yeah, the fireplace is fun.

Joel: it's 

Collin: Uh, Yeah. 

Joel: It's cozy.

Collin: Very cozy. 

So we're gonna be at RubyConf in, in one week. You and I will be hanging out. It'll be the first day. We'll be going around trying to get everybody, trying to get people to go to our, our, our live episode probably.

Joel: Probably, uh,

Collin: Yeah, if you're listening to

Joel: whole, like, trying to recognize people from their avatars. Which aren't necessarily very up to date or even off their face.

Collin: I don't think it'll be too hard. Uh, everybody's going to have their name written on their badge. So I think, I don't think it'll be too hard to meet up with people. Um, but yeah, so we're going to do that then. Uh, so Monday we'll just be hanging out, I guess, at the community day.

And then Tuesday will be the, we're going to do the live podcast episode right after lunch. So, so I was going to say, if you're listening to this, please come. We want as many people to come as can come. And then on Tuesday from 6 to 9 at Monkey Bar at the resort, uh, we will be having our party, which is fun.

Joel: The rooftop party.

Collin: the, the rooftop party that's on a lawn. I'm very excited about that. But, uh, you know, please come because we're gonna have free drinks. And so I think that'll be very exciting for, uh, for everyone involved. Um, yeah. 

So is your, is your web framework done?

I know you were working on it last week, but, 

Joel: It's getting there, it's getting there, it's pretty close. 

Collin: okay. 

Joel: I, I think it's, it's probably going to take... I, I, I mean, I'm as bad as the next developer at SoftwareEstimates, but it feels to me at this stage like it's about 10 flexes, and flex took about a year. 

Collin: Mm hmm. So in about 10

Joel: 10, 10 years. 

Yeah, no, being realistic though, I think, I think it will be sooner than that.

Um. It's not just me working on it, so that's good, and um, I feel like when you, when you compare with someone who you just work really well with, you're like more than two times more productive at, at certain kinds of tasks. Like when you're tackling the really, really complex like design type tasks where getting that feedback really quickly.

And being able to just throw loads of ideas into the mix, and see what works, see what doesn't. Like, yeah, I don't this doesn't quite feel right to me, and then someone else has another idea. Like, it's it's just I find for this, especially really early on, when you're trying to design, like, how does this even look?

What does it feel like to use this framework? That's that's really important, and so I think, um, I think that, well, I'm hopeful it won't take too long, and maybe we'll have a, some kind of alpha ready in the next few months, but we'll see.

Collin: Yeah, I totally agree with you about it being more than a 2x improvement. Like, even just, just having somebody that is very responsive that you can talk through things with. Like, the guy that I am working with currently is very responsive. And so, um, when I need to think through something or talk about something, I'll usually just be like, Hey, can you hop on a call really quick?

And most of the time. Uh, he is available or, you know, we can do it a little bit later in the day and we just talk through it and it's, it's been really good to like, just be able to bounce things off or a lot of times he'll, you know, he'll have already done something in a way and then be able to suggest that and then that's very helpful.

So yeah, I absolutely agree with that. Um, so, so what parts were you working on this, this, uh, so far?

Joel: Yeah, so, we're trying to tackle, initially, uh, routing and the ORM. So, we feel like those are probably the two most complex parts of a web framework, and also the most essential parts of a web framework. I think the next thing after that will be deployment, 

Collin: Mm 

Joel: is, I guess, something that you could say you don't even need to tackle.

And, you know, we could be like, okay, well, like, initially, maybe people would just deploy this using... Heroku or something, or existing tools. But, um, I think because of the, the database architecture that we're trying to use, where you have maybe thousands of databases, um, and also because we're trying to make the experience as, like, streamlined as possible, like, you install the framework, you generate an app, you maybe put in your DigitalOcean key, And then you have a server, right?

And, and your server is ready for people to visit, and they can, you've got a landing page, they can put their email address in, it's already collecting their email addresses into a database, everything's all set up for you, like, straight away. Um, but yeah, this, that, that's, like, gonna be the, the kind of the next big thing, uh, is, like, figuring out deployments, because we want to do, like, So, zero downtime deployments with zero downtime safe database migrations across thousands of database files, uh, across, you know, maybe three, four, five different classes of database that you might have in your application.

Uh, so yeah, this is, that's, yeah, that's probably like the next, the next big thing, but initially we've just dived in and... Got, like, the basics of the router working really well, um, and we're working our way through the, the ORM. We did consider, for a while, uh, just using an existing ORM, and we looked at a bunch of different ones, and there were some really great ORMs for, for Ruby, but, um, I think that we're going to be able to build something.

That is more suited to the kind of application we're building. Like, I don't think many of these other ORMs were designed to work exclusively with SQLite, and, or, like, or designed to work in a way where, like, you have so many databases on the, on the go.

Collin: mm hmm. Thank you. Yeah, the whole thing you want to do is kind of based around a unique idea of how you want to use SQLite specifically. And so it seems like having your own ORM for that was probably going to be necessary.

Joel: Yeah, yeah. Um, God, ORM's really difficult to say, isn't it?

Collin: Yeah, ORM. Yeah. Or, yeah,

Joel: The database layer,

Collin: the database.

Joel: yeah, it's, it's fun though. Like it's, it's nice being able to kind of look back at years of experience building applications and try to just think about how would I do this? If I was starting from scratch, because I am starting from scratch

Collin: Mm hmm.

Joel: like. You can, you can put in constraints early that are really difficult to merge in later. Like the idea of having immutable database models. I don't think you could add that to Rails, even if you were really motivated to. That would be an incredibly difficult challenge.

Collin: It's not really built around that.

Joel: Right, and, and, and, you know, Rails is, is trying to add a lot of stuff around concurrency now.

Uh, and they're doing quite a good job, but like, you can still tell it's, it's got some pretty rough edges. But again, like, if you are building from scratch, and like, high concurrency is a top priority, like, I think, I don't know, it's, it's just like, you get kind of, from these constraints. Sometimes you come up with different designs that are different, but they still work really well.

And that's kind of different to trying to shoehorn a constraint into an existing design, I think.

Collin: Yeah, I mean, it would have been difficult in, like, 2004 or whatever, right? To predict, uh, the idea that concurrency would become more... Important, I guess, or whatever, um, where if you're starting now, you would just think about it totally differently because that's, you know, you're going to be working with like multi core machines and different sorts of, uh, patterns that are more established now, right?

Joel: right.

Collin: So, so the router's kind of working, it sounds like. What's, I, I've never done this. What's involved in building, building a router for a web framework?

Joel: Yeah, um, not a, not a ton, to be honest. The, the router that we are... Sorry, I say, I say router because I'm British, but um, router. The, the router that we are building

Collin: I'm going to say router now.

Joel: Yeah, it's, it's...

Collin: We should switch every time one of us says it.

Joel: So, it's very much built on, um, the ideas from, uh, Roda. Which is that you have this kind of, like, rooting tree.

And, uh, how that works is... When, when you get a request, you kind of, like, execute a proc. And then that proc decides based on like the, the part of the, the path that it's consuming, whether to send it to one of like the next few procs and then they decide whether to send it to another p proc. Um, and so it's kind of like a tree of the, of executable code.

So you can't actually build up, um, like a tree that you could analyze or, like, visualize. There's no way to do, you know how in Rails you can do, um, bin slash Rails routes?

Collin: Yes.

Joel: And there you go. I said it the right way for our listeners. Um, and it gives you like a full list. I don't think there's going to be a way that we can do that.

Because of, because of the, the routing tree, um, but it does keep the kind of architecture very, very simple.

Collin: Mm hmm.

Joel: If that makes sense.

Collin: Yeah, what does the backtrace end up looking like for that if something goes wrong in one of those procs?

Joel: It's, it's quite, it's quite good actually. It just, it tells you, um, okay, so I, I should probably explain kind of how, how you would set this up. So, in the most basic sense, uh, you could create an application in one document, right? You could just have an application class, and you have a method that handles a request, and it can say, uh, it can match certain things about that request.

So it can say, if the request contains this, I want you to move into this proc. If it contains this, I want you to move into this other proc. Um, and it can also say, if the request is this thing, then return. Like, this response. And so you can kind of just, you build up this, like, tree of blocks within blocks within blocks within blocks.

That's, like, quite good when you're getting started and you're prototyping something really quickly. But eventually, it gets really, really unwieldy. Difficult to manage. Um, so what we're doing that's slightly different to Roda, and Roda has like a different approach to kind of handling this issue of like your application file getting too big, but what we're doing is we're saying you can have other controllers that are callable.

So you can use a controller class as though it's one of these procs. So you might say, um, root requests to slash articles. To this articles controller and you just put ampersand behind it and you say articles controller and the ampersand says call the to proc method on this constant

Collin: On the 

Joel: And treat that as the block on the controller class.


Collin: Mm hmm. 

Joel: So then we have these controller classes that are designed to kind of handle that They're part of the request right and they can also delegate down So maybe your articles controller might delegate down to the comments Or it might delegate down to an article controller that's designed specifically to handle just one article.

So that might be, it might handle the roots for show, edit, and update,

Collin: Right.

Joel: if that makes sense. 

Collin: Yes, that'll make sense.

Joel: yeah, so that's, we've, we've tried to make it so that you can, you can kind of switch between those two different Um, and then once you get down to a controller that's going to actually handle requests itself rather than just delegate them off to other controllers, uh, you would then, um, you would have a proc that would say something like get slash edit, and then in the block you'd say maybe edit, and that would call the edit method, and the edit method would say stream the edit view.

Collin: Mm hmm. I think that all makes sense. Yeah. And so you end up with kind of this tree of things

that sort of resolve to what you want them to be. And it's kind of flexible.

Joel: Yeah, and from my experience using it so far, and it's pretty limited, it is quite easy to debug, because you can, you can see from the backtrace, um, you know, it went, it took this route through the application. It hit this line, and then it went into this file, and hit this line, and it went into this file, and hit this line.

Uh, and so, you can, I think debugging should be quite, quite straightforward.

Collin: Yeah, no, that makes sense. Um, yeah, because it's going to flow through all of your classes or whatever.

Joel: Right, and, and it's all just normal Ruby. We don't build up any kind of... A tree that has, you know, logic and like, match things, match the path in any kind of unpredictable way, um, so you don't have to understand, like, the internals of some, some black box routing magic that's going on behind the scenes.

It's pretty reasonable to just think about it as, um, you know, conditionals in prox that are calling other prox, essentially. Like, it's pretty much literally implemented as, take the first part, the next part of the path that hasn't been consumed yet, so maybe that's slash articles, and check if it matches this pattern.

Like, is it this string? Or is it, um, this regular expression? And then, if it does match... Return that.

Collin: no, that all makes sense. And then the trade off you said is that you cannot use that to sort of get that, like, rails, routes sort of thing.

Joel: Yeah, I think we can probably get close to it by using parsing, so we could probably have, uh, at least for the majority of, like, if you stay within the kind of standard pattern, we could probably parse the files to give you a pretty good representation of your tree, uh, but yeah, it's, it's not very precise.

Collin: It's not gonna be, like, introspecting, 

Joel: I don't think so. Yeah. Um, there are probably optimizations we can make for specific paths that are static. Um, where we can say, actually, we're going to take, you know, more chunks of this, this URL, this path, and, um, like, sorry, we can take that, the entire path, and we can put it in a hash. And then you can use, like, O1 time lookup, 

Collin: Mm 

Joel: routing, which is obviously going to be the fastest possible routing, but is quite inflexible because you need to have all of these, like, hashes known up front.

So you can't do, you can't have a hash that contains, you know, a dynamic attribute such as an article ID. Um, but yeah, for certain, for certain routes we'll probably have an optimization where... Uh, where we will, like, basically just cache that specific root resolution and then store that so that the next time someone comes along, they can take a quicker route.

Um, the problem with that though is if we do that, it will mean that the, the path that someone takes to get to a specific route will be different if it's cached. So it, they could end up being routed, um, to a different resource. So if you could imagine, maybe you have some Ruby code that says. If the, if it's 12 o'clock, then route them here, otherwise route them to this other place, right?

And the first time they go it is 12 o'clock, so that gets cached, and then the second, the second request, um, even though it's not 12 o'clock anymore, is still gonna route to that, that location. So I think it's, it's kind of risky, but I think it's probably a performance optimization that you, you'll be able to opt into.

Collin: Right. And, and, as we know, cache invalidation is famously one, you know, very easy. Um, so that makes sense.

Joel: yeah, exactly.

Collin: Okay, so then you're also building the ORM and, uh, I don't know if you want to get into that or not, but, like, So, your idea about, around this ORM is that you're going to very easily be able to work across a bunch of different files in different locations, like, How's that, what, how?

Joel: So, The ORM will, when you query the database, in a Rails application, you don't think about what database you're querying, usually. You just think about, like, what the query is. Like, what tables do I need, um, and what am I selecting? And, well, you don't even think about what am I selecting, right? Because Rails just selects everything, and everyone just uses it where it selects everything.

Um, so... It's going to be quite different in yuppie. The first thing is, uh, you have to think about which database you're querying. So, um, you could just have one global database for your entire application, and that's probably fine and will serve you well for a really, really long time. But you can begin to split off, um, databases for specific tasks.

So we will provide databases for, uh, job queues, um, and for like pub sub stuff. Uh, and we'll also have some, like, kind of framework level databases for managing, uh, managing the databases, basically. Like, databases of databases that you probably won't need to think about. Um, but then, but the other architecture you can use is you can say, I want to have my main database identify a tenant.

Maybe that's by routing the URL that they visited. Maybe they visited a subdomain, or they have their own domain name. Um, and then once it's identified them, It will give you access to an instance of their tenant database, right? And this will be like an instance of a class, essentially. It's going to be a database that follows the same schema as all of the other tenant databases, but this instance only has the data for this specific tenant in it. Um, and then basically, so, so, so when you start querying for something, you, you're thinking, Which database am I going to send that to? That's, like, basically the first step. Does that make sense?

Collin: Yeah, so you've got to sort of have some awareness of this level. If you want to be taking advantage of that.

Joel: Yeah. And it has some advantages. So, one of them is it lifts the roof on, kind of, performance concurrency, um, concurrent writes, um, like, there's kind of limits to how many concurrent writes you can do in SQL, uh, SQLite, and um, the more databases you have. Um, the, the more limits you have, essentially.

Um, but I think that the more powerful argument for doing this is you get really, really good, by default, kind of tenant, um, what's the word I'm looking for, like,

Collin: Encapsulation,

Joel: that's right, encapsulation. So, it's really difficult to... accidentally expose one person's data to someone else

Collin: because they're in different 

Joel: it's a completely different database.

Right, exactly. Uh, so I think that is like, for me, that's the main thing is being able to, like, not have to worry too much about At every time I'm querying the database, make sure it's scoped to the tenant, because it might not be scoped to the tenant. Um, but yeah, so, like, but, I mean, that aspect is quite, it's quite easy.

Like, we can, you'll essentially have, uh, an instance variable of the database, and then you just send methods to that specific database instance. Um,

Collin: So can I ask you a silly question? Um, okay. So do you foresee any challenges here then when you do need to reach across? Like, so I'm thinking. Uh, you have an admin panel, right, for the people running the app, like, you know, the, the owner of the application, and they want to see everything across all the databases, or have access to it, like, do you foresee any challenges with that?

Joel: Yes. I think... So, in order to see... Anything across all the databases, you need to have summaries collected asynchronously from those databases, I think. And that could be a case of saying, uh, every day I want you to, for every single database, give me these statistics and, like, put them together and put them into the main database

Collin: Mm

Joel: for the admin panel.

I think that... Use case is for many applications, not a big deal. Um, you, that you don't need to be looking into specifically what a tenant has going on. Um, and what you can do is you can collect your statistics in different ways. Like if you think about like, maybe you want to know how many, um, I don't know how many comments has.

I have all my tenants posted, or has each tenant posted. So you could, um, you could try to, like, go through all the databases and count all the comments. That's gonna, that's,

Collin: Mm hmm.

Joel: like, one way you could do it. Um, also, alternatively, you could add some metrics, uh, and you could just store an event when someone adds a comment.

And those events are kind of, like, grouped and shown on your admin interface separately. And, like, that's probably close enough to what you need for, for that kind of thing. I think, I think it really depends. Um, and, and there will be ways that you can do kind of, uh, cross database joins. But, I think for the most part you're not going to need to do that.


Collin: I mean, I suspect whatever the challenges are, you will overcome them. I was sort of thinking of, like, the circumstance where, you know, you have a bunch of tenants or whatever, and then those each have users within them, and maybe, as the administrator of this whole thing, you need to be able to get in there and, like, change permissions or something.

You know, that kind of a thing. And that felt to me like it would... You would need, at some level, to be able to see just, like, the state of everything.

Joel: Mm hmm. Yeah, I don't know exactly how you would deal with that. Um,

Collin: Yeah, you've been working on this for like two weeks. I don't think you have to have answers for everything. That was just something that I thought of when you were saying it.

Joel: No, it's, it's good to think about these things. Um, there, you can, of course, do very basic relations between, uh, between databases. Like, a lot of the queries that, like, association queries that you run in Rails will do one query for the initial set.

And then they'll take all of those IDs, and they'll just send them to a second query, where you'll say, like, give me all of these, where the ID is in this list of IDs, right? And those two queries to two different tables could have been to two different databases as well, like, quite easily. Um, obviously, like, when you're actually joining tables together and things like that, you can't, you can't do that between different databases, but...

For a lot of kind of like association type things, I think it's going to be pretty straightforward to do that with, uh, with multiple databases. Yeah. The real challenge has been building a really nice kind of ergonomic Ruby interface to building up queries. Um, and we're trying to find a balance where we don't try to do too much in Ruby, but we try to make at least all the basic things that are kind of 80 percent of what you do should be really, really straightforward and easy to read and easy to work with in Ruby, particularly around composition.

Like, I want to be able to say, here's a relation to... Uh, a table in a database, some, some rows in a table in a database, and I want to combine it with this other, uh, you know, filter, right? So that I can kind of compose and combine these things together. And you can do that quite well in ActiveRecord,

Collin: Yeah.

Joel: I think we want to be able to do that in YIPPY at the same time, we don't want to make it too complicated, and at a certain point, I think, You want to just go and do SQL.

Like, we're trying to make it so that stepping back and saying, Okay, now I'm doing something that's quite complicated, and I'm gonna need SQL, like, that experience should be really good as well. So, being able to import a SQL file, um, and use that, being able to pass SQL in as strings, uh, at various points in the query builder, like, that's all supported as well.

Collin: Yeah. I want to always be able to get at it from both directions, ideally, right? Like. 95 percent of the time plus, I would prefer to not be writing SQL because it's more dangerous. I could screw it up. I'd prefer to use like the things that the ORM provides me. Uh, but in the other small percentage, I don't want to be bending over backwards because the ORM doesn't let me just give it SQL sometimes.

Joel: Mm hmm. Yeah, that makes sense.

Collin: yeah, and I think, I think with building. This with the ORM, it's, it's kind of nice because on one level, you guys are starting from scratch and you have kind of like a green field thing. Yeah, you can come up with however you want to do it with like immutable objects and concurrency in mind and all of that, but then also you have a lot of really good examples, not just in Ruby, but in.

A million other languages and frameworks for kind of what works and what doesn't. So you sort of, you sort of can pull from many things to create something that fits what you want.

Joel: Yeah. Yeah, that's definitely true. I think, I think that the big distinguishing thing between Yippee and Rails will be, like, Rails tries to make complex things seem easy, and then you realize that they're complex later. I dunno if that makes sense, but like, we're trying to, I guess just take a slightly different approach.

Like, um, I can't, I can't think of a good example, but, so for example, um, you should really be selecting which columns you want to pull from your database when you run your queries. But most Rails apps are querying for. Like, they're selecting star, like, give me all the columns, all the time, right? Because it's what the ORM really strongly encourages.

In fact, like, it's kind of a bit weird using Rails with selects because you end up with models that don't kind of, don't behave the way you expect them to, and we decided that we want to make, like, selecting the columns be something that you have to do. Like, you will be able to say, select star, but that will be a choice that you make.

It's not implicit. You can't just say, like, give me all of my, uh, records from this table, and it will select star for you. You have to say, select star from, uh, from this table. Um, and I think, like, just that design decision, I think, is gonna be really critical to, like, how, what, the kind of application that you end up...

When you build with Yippee versus building with Rails, um, it's where, where maybe you've taken a bit more time to do it, but you've thought through every single thing, if that makes sense. So, I, I don't know if maybe, like, you'd be able to make, I don't know, the quick 10 minute blog. Maybe, maybe you could do that quicker in Rails, but in Yippee, hopefully. You will have made more decisions along the way, and you'll be really happy with those, dec those decisions, and they'll, and they'll stay with you and they'll serve you well for years to come. We're trying to make building really good maintainable software easy, if that makes sense.

Collin: Yeah, no, that makes sense. I mean, building it with the exact same goals as, as Rails or whatever wouldn't really make sense because Rails already exists. So if you're going to build something, you should. You know, have your own spin on it. So that that all makes sense that you're taking a very kind of different Viewpoint towards a lot of those things 

So I Had a question for you because so I was thinking I've been thinking about a couple of things as well this week or in the last couple weeks, which is one of them was and maybe I'll cut this out of it makes me sound too dumb, but One of them is when you are working in your Rails app, right?

And you have your controller and all of that, what is your instinct around like resourceful routes, like CRUD routes versus, uh, like custom things? What is your like instinct around that? Cause I think I have been thinking about this.

Joel: Yeah, that's, that's a very good question. That's a very, very good question. I think if you had asked me that two years ago, I would've said. They should always be resourceful. You should always just create another controller and use the index or whatever it is on that controller instead of adding another root to your main controller.

Um, I think that my thoughts on this are currently being challenged by, um, things like, um, TurboBoost. Uh, and also just, like, thinking through... So, how we should do, like, I think TurboBoost calls them kind of reflexes, which is where if you have some kind of user interaction on the front end, and you want the server to be able to respond to that event, with some, like, very, very discreet Response.

This's maybe just gonna change a number on the page, right? Or a button, or like add a comment, something really, really small. This is all like kind of, uh, what, what you can do with Hotwire and um, turbo streams and that kind of thing. Um, I think. I'm beginning to come round to the idea that these are distinct from resourceful type roots and there should be a place for them as well.

That's a kind of long winded way of saying, I'm not sure.

Collin: Okay, good. Because 

Joel: depends. 

Collin: so, I'm not sure either, but I have been thinking more in terms of leaning on Resourceful. Uh, Roots, as you'd say. Um, in that, so here's an example. I'm working on some event planning software, right? It's no secret. And, so I have an event model, I have a guest model, and it's kind of a many to many relationship for reasons I don't need to get into.

So I have an event member, uh, thing in between them. And, I was working on creating basically tickets and like passbook passes, that sort of thing. And I thought, Oh, these could hang off of the event member, right? Could hang off of the, the join. And I started going down that route and then. You know, just, it, it was, I realized there was going to be a lot of like conditional here and needing to like put into a template or something.

Like what, you know, what is the state of things? It was getting kind of gross a little bit. And then I was like, Oh, I should just add a new model and a new controller here for like representing a ticket. And I don't know that that is a, that's kind of an obvious example. Like, of course you would do it that way, but it just, it's the most recent one.

And sort of thinking for me in terms of like. If I am tempted to create a custom route, I'm trying to stop for a second and think, can I logically map this? If I had another controller, I had another like domain model, could I logically map this to something else? And so instead of having a like, MakeTicketRoute.

It's obviously not what you'd call it, right, but let's say I'm having like a route special for tickets or something, living in, I don't know, my guest controller or whatever, right? In this example, being like, oh, I could have a tickets controller and then there's a create route on that and then I could access it in that way.

And then the other thing that made this nice is that, uh, when I did implement it that way, then I ended up using TurboFrames so that... So then, like, you know, you get the little inline editing view and stuff, so it's, it's going to there, but it's still displayed kind of in a nice way. Um, I don't know, so that's sort of what I was thinking about this week, is when I am tempted to create a custom route to just stop and be like, alright, how...

If I was going to map this to something, like, what would that look like? And I don't think it's a rule. Like, I think having a non, you know, a different route is fine, too, sometimes. But I don't know. I've just been trying to be a little bit more thoughtful, I guess is what I'm saying.

Joel: Yeah, I think that's a really good intuition, that you should try to follow that pattern as much as you can. And I think that you'd be surprised how many places you can apply that to. Like things that you might think of as just being, I guess, events,

Collin: Yeah.

Joel: can end up being resources. Like, uh. Maybe, just off the top of my head, um, deleting something, right?

Say you've got this, um, you've got this event model, and you want to allow the users to delete it. If there's a confirmation step involved in that, maybe you have a new deletion view, as well as a post deletion, like create deletion

Collin: Yeah.

Joel: endpoint, right? Um, and maybe even you use the show endpoint to give the message after they've deleted it.

So, like, you could, you could model an event deletion, essentially, in your controllers, and have an entire controller dedicated to it with new post 

Collin: Um, so I think you nailed it. Uh, I think you actually explained it better than I was, which is, yeah, there's a lot of places where I'm thinking, like, this is an action. And so sort of my first gut reaction is to then, uh, you great. Non standard route that like matches that action, and I'm trying to be more thoughtful then and say like Can I map this the way you said like to a different kind of controller action where maybe like yeah There's a removal controller, and then I'm creating a removal or whatever like that kind of thing like you said so yeah That's that's exactly What I was thinking of so I'm I'm glad to hear you you know you validate my thinking on this

Joel: I think the same kind of technique you can use With other parts of your code as well, like turning an action into a thing, so turning delete into deletion,

Collin: Mm hmm.

Joel: right, allows you to add a lot more richness, a lot more complexity to that. Um, and that, that could be the same in your model layer, right? It's, it's, we might have a method to delete a model.

Um, but maybe the process of deleting a model is so complex, you know, if you, if that method is then depending on 10 different private methods that it's passing, like, various things around between, like, you should extract that into some kind of object that models event deletion itself. Like, that process is to be modeled.

Um, and you can, I, I'm I'm all for classes that you just throw away. I have no problem with creating classes and throwing them away. 

Like, create a class for this thing, put all of the arguments that you would have sent to the method in that class's initializer, set them all to instance variables, and then define a call method, and you call that instance method, and then it can, it can do all that internal stuff without having to pass state around, uh, and then... Like, I don't know, it's just, I think that that kind of way of thinking about turning some action into an event is just a really powerful thinking tool that you can apply to all sorts of different places.

Collin: Yeah, like in, in the model example, that's probably where my instinct would be to, uh, have a concern that kind of represents this, uh, this type of a thing, you know, represents, you know, whatever the deletion thing or something, uh, and then that's really just a front end to like this whole subsystem of objects or whatever that do what you were saying.

Joel: Right.

Collin: And so you're still speaking to it through your model, but it, you know, you're still, you were still sending the action through there, but then it has this whole kind of cascading thing going on. That's probably my instinct there.

Joel: I think that concerns have a place somewhere between the kind of private method and the service object or whatever you want to call what I was describing. Um, but they are limited. So they do share, like when you mix in a concern, you don't have a new scope. So like any methods that you define on your concern module are available in the main model, right?

And you're, you're moving them to a different file, but you're not, and, and that can be helpful, like, I'm not denying the, the, the utility of that, but you're not actually moving them to a different context where they have maybe access to fewer things, or maybe quicker access to the things that are important and relevant.

Um, and so, like, if you have three or four methods in that concern, they may still need to be passing loads of attributes around, and maybe you could model something that, uh, That takes as an argument the object you're working on, as well as all of the parameters of that action you're trying to process.


Collin: no, that's, so that's actually kind of what I was trying to express. Maybe I didn't do it very well, which is I would have the, um, I would have sort of the concept represented through an interface that is the concern, right? So that I've said like this. This concern, if it's, uh, namespaced into the right area, like this is what it represents, but then, uh, I'm not just adding all those method, you know, every method I need in the module to my, you know, to my current class.

I'm not just mixing them all in.

Joel: Oh, the mod the module's constructing another

Collin: yes. Yeah. That's constructing basically the service object thing you were talking about. I just like getting to it from, through the model, uh, because that's like a. Easy way for me to think about it. But yeah, I'm not just like mixing in a thousand, you know new methods necessarily

Joel: Yeah.

Collin: Unless I am I don't know I 

Joel: I like that we have all these different layers, like, you can start with, just do it inline, and then you can start with, like, put it in a variable, and name that variable well, and then extract it into a method, Uh, that's private, and then maybe extract it into a concern, and then extract it into an object.

And you can, the same kind of functionality can go through all of those different layers depending on how complex it is, um, and I, I, I think that's just one of the richness, a rich feature of Ruby, that you get this kind of, like, different ways to do things. Depending on, like, there's an accordion of complexity.

Collin: Yeah, I That's definitely the show title I Yeah, I 

Joel: That's a, that's a quote from, from Steven.

Collin: While we're stealing it, um, so Yeah, I, uh, no, I agree. And I, I, I like the, uh, you know, being able to start at the kind of just get in there and make a mess level. And then, 

Joel: Right. 

Collin: you know, as it, uh, as it makes sense to sort of refine this down and as it becomes more clear to you, you can use all of these different tools to create something really, uh, to make a system that really makes sense.


Joel: Mm

Collin: mean, that's not exclusive to Ruby, but I like Ruby a lot.

Joel: hmm. Yeah, me too.

Collin: Yeah, so I don't know. I just, uh, everybody come to our party and come to our talk because, uh,

Joel: come say hi to us.

Collin: Yeah, and come say hi to us, uh, you know, get a sticker or whatever, um, yeah, I just, I'm really excited just to meet, uh, you know, more people.

I'm excited to meet you. That's going to be the most exciting part, obviously, but, uh, I'm also excited to, um, you know, to meet lots of other new friends at, uh, at, at the conference in a week.

Joel: Yeah.

Collin: Cool. All right, well, we will see everybody there. 

yeah, and that's... So, so 

Joel: Sorry, one second. I 

Collin: Okay.

Joel: think I've just started Siri. in my kitchen, so I'm gonna go and stop it.

Collin: Okay, I'll sit here.

Joel: Okay, one sec. Okay. 

Collin: All right, great. That's definitely going at the end of the show. Um,

Joel: Dingus has been silenced and unplugged from the wall.

RubyConf Next Week
Resourceful vs Custom Routes

Podcasts we love