Rails 8.0.1 You are in good company

杰瑞发布于2025-12-31

Over the past two decades, Rails has taken countless companies to millions of users and billions in market valuations.

Now, let's add a bit of custom JavaScript. Rails by default ships with Hotwire! Hotwire gives you Turbo, which is a way of accelerating page changes and updates is that your application will feel as fast and as smooth as a single page application without you basically having to write any JavaScript at all.
And then, there's the stimulus framework for creating that additional functionality that you might need in a really simple way. You can have a look at hotwire.dev to see more about that, but what we're gonna add here is a little piece of JavaScript to just add some additional functionality, pulling something in from pin.
bin/importmap pin local-time;
So, we can do that using the import map pin command. And as you see, now that I hop back into our config import map, we've added the local text pin at the bottom, version 3.0.2. It pulled that straight off NPN, it downloaded that as a vendor dependency that we can check into our version control system. And now, we don't have any runtime dependency whatsoever on NPN, or anything else like that.
digesting:
You don't need anything beyond what Rails ship you with already, because Rails 8 is all no build by default! That means there's not a transpiler, that means there's not a bundler, these files are shipped directly to the browser over HTTP2, so it's nice and fast. And the import map is what allows us to refer to these files by their logical names while still doing far future digesting, so that they load really quick,and such that they're easily compatible with CDNs and all that good stuff.
But now that we've add that, let's have a look at our application JS file.That's the default setup that you have that the scaffold is going to use. And as you can see, we're using turbo-rails, we're including all the stimulus controllers, if we have any.
We're including trix and action text to give the WYSIWYG, and now we're gonna add that local text package as well. "import LocalTime from 'local-time' LocalTime.start()"
And we're gonna start local time here. And in the local time, we're gonna use it, and we're gonna use it for adding the updated at timestamp here. And as you can see here, we're just adding a time tag that's just a vanilla HTML tag that has a data local time that's what activates that local time JavaScript set up.
<%=time_tag post.updated_at, "data-local": "time", "data-format": "%B %e, %Y %l:%M%P" %>
zoo:
And we will give it a format for what it should do with UTC timestamp, and turning it into a local time that we can have a look at.So if I reload here, you see it is November 13th, by the time of my recording at 3:28 PM in my local time zone, but actually underneath, the time tag is gonna be in UTC. That means we can cache this, and anyone around the world will still get the time displayed in their local time.
Just a nice feature.
inspector:
But really, what's unique here for Rails is the fact that we're using no build by default! So if I go over here in the inspector and look at the JavaScript files that are included,you can see we have the application js file with a little digest stamp on there. If we change anything that application js file, the digest is going to change, and the browser will redownload just the part!
12:47:
but everything else, turbo and stimulus, if they're not changing, we're not downloading that. That is why no building is such an efficient way of dealing with caching.
But we can also have a look at a specific file, you'll see it matches exactly what we have back there. That's not a development setup! That is what we're shipping in production. There is no minification, there's no transpilation, there's none of that nonsense because you just don't need it.
We're G sipping or Brotliing this stuff so that it transports really quickly, but we're allowing you to view source on an entire application.
If you look at something like hey.com, you'll see this technique in use on a major application, and you can view all of the Javascript that we use to build that application, and that's the default for Rails.
Now again, if you don't want any of this stuff, there is a way using JS bundler dash Rails to set things up in a more traditional way using ES build and what have you. But this is a wonderful way of developing modern web application.
bin/importmap pin local-time
rails g resource comment post:reference content:text
Alright! Now let's add some comments to our blogging system! And I'm gonna use a different generator here,I'm gonna use a resource generator that is a little lighter than the one we were using for scaffold that doesn't generate a bunch of views, and doesn't generate all sorts of actions in the controller by default, but it does generate the new model that we need the comment model, it generates a migration for that, create comments ,and it generates just some empty placeholders for the comments controller and for the view action.
So, let's run the migration for that, that sets up the comments table. You can see here the schema that we've now built up. We've added a number of tables for action text and action storage. And then, we have added a comments table. That's what you can see here. As we had it in the migration where we were just referencing the post as a foreign key, and then we had the content as text.
And then, Rails by default also adds two timestamps that it keeps track of by itself, created at and updated at. And below that, you had the post that we added originally.
Alright, if we hop into that comments controller, it was empty. As you can see there, I'm gonna pay something in that actually makes this stuff work! You'll see one principle of the controller setup we have is that we have these callbacks. Before action, we're gonna set posts. So before all the actions, we're going to reflect the fact that this is nested resource.
The comments is something that belongs to a post, and we will pull out the post ID from the params, that's what's being parsed in as part of the URL, and we will fetch that post, and now we will create the comments associated with that post based on the parameters that are expected as comment content. And then after it's created, we will direct back to the post!
So let's actually also create the other direction of this association. You saw a comment belongs to a post, but then we're also gonna make the post has many comments. Now, we have a bidirectional association that we can work with in both ways.