<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:fh="http://purl.org/syndication/history/1.0"><channel><title>Rob Race | Writing</title><description/><link>https://robrace.dev/</link><language>en</language><atom:link rel="self" href="https://robrace.dev/blog/rss.xml"/><atom:link rel="prev-archive" href="https://robrace.dev/blog/rss/2026-08.xml"/><item><title>Programming Tutorials Are Dead</title><link>https://robrace.dev/blog/programming-tutorials-are-dead/</link><guid isPermaLink="true">https://robrace.dev/blog/programming-tutorials-are-dead/</guid><description>AI did not kill developer education. It killed the old workflow where learning ended with a sample app and the developer did all of the translation work.</description><pubDate>Thu, 17 Sep 2026 07:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I sell programming education.&lt;/p&gt;
&lt;p&gt;So this is not a particularly convenient opinion for me to have.&lt;/p&gt;
&lt;p&gt;Programming tutorials are dead.&lt;/p&gt;
&lt;p&gt;Not literally. Somebody will read a Rails tutorial today. Somebody will buy a programming book this week. I will probably write more tutorials myself. What is dying is the old contract around them: I build a clean example application, show you how I implemented something, and then you do the work of translating that example into the application you actually care about.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-revenue-is-already-trying-to-tell-us-something&quot;&gt;The revenue is already trying to tell us something&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I have sold developer books and other educational products for years. I know what launches used to look like, what warm-list sales looked like, and what happened when an article started ranking and pulled somebody into the rest of the catalog. My own gross revenue from traditional developer education has been moving in the wrong direction.&lt;/p&gt;
&lt;p&gt;That is one person’s business, not an industry report, and there are plenty of ways I could explain it away. Maybe the audience changed. Maybe the market is saturated. Maybe I should write better emails.&lt;/p&gt;
&lt;p&gt;Then Chris Oliver published a pretty blunt update about GoRails and Hatchbox. Chris has been teaching Rails developers for more than a decade, and GoRails grew from tutorials into a business that helped fund Hatchbox development. This is not a creator who posted three screencasts, had a bad launch, and decided education was broken.&lt;/p&gt;
&lt;p&gt;In September 2026, while explaining a Hatchbox price increase, &lt;a href=&quot;https://www.linkedin.com/pulse/bootstrapping-hard-especially-now-honest-update-hatchbox-chris-oliver-pthtc&quot;&gt;Chris wrote that AI had been brutal to education businesses&lt;/a&gt;. He said GoRails had historically subsidized Hatchbox development, that this was no longer sustainable, and that GoRails would have fewer new customers in 2026 than it had in its first year.&lt;/p&gt;
&lt;p&gt;That got my attention because it looked a lot like what I was already seeing, and like how I was already behaving as a developer.&lt;/p&gt;
&lt;p&gt;I am part of the problem.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;i-do-not-want-to-build-your-sample-app-anymore&quot;&gt;I do not want to build your sample app anymore&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The traditional programming tutorial has a hidden translation step. The author starts with their application, reduces the problem into something teachable, and builds a sample app, article, screencast, or course around it. Then the reader has to move all of that knowledge back into their application.&lt;/p&gt;
&lt;p&gt;The flow looks something like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;expert&apos;s experience&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;tutorial application&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;your eyeballs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;your brain&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;your keyboard&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;your application&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;For a long time, that was just how technical learning worked. The sample app has &lt;code dir=&quot;auto&quot;&gt;User&lt;/code&gt;; yours has &lt;code dir=&quot;auto&quot;&gt;Account&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;Membership&lt;/code&gt;. The tutorial uses Devise; you are using the Rails authentication generator. The author starts from &lt;code dir=&quot;auto&quot;&gt;rails new&lt;/code&gt;; you are adding the feature to a six-year-old application that has already accumulated several generations of perfectly reasonable decisions.&lt;/p&gt;
&lt;p&gt;The tutorial tells you what worked in its environment, and you figure out which parts survive contact with yours. You were always the integration layer.&lt;/p&gt;
&lt;p&gt;Coding agents changed that.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-agent-already-lives-on-the-other-side-of-the-tutorial&quot;&gt;The agent already lives on the other side of the tutorial&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A capable coding agent can inspect the application where the work is actually going to happen. It can see your models, tests, authentication setup, jobs, naming conventions, existing abstractions, and the weird compatibility code nobody remembers adding. It can search the repository before deciding where something belongs, implement a change, run the tests, inspect the failure, and try again.&lt;/p&gt;
&lt;p&gt;That changes what I want from somebody teaching me.&lt;/p&gt;
&lt;p&gt;Say I want to add reliable webhook processing to an existing Rails application. I can watch somebody build it from scratch in a clean app, pause the video, copy the relevant pieces, rename everything, adapt their assumptions, notice that my queue setup is different, search for the part where they discussed retries, and eventually get the implementation into my codebase.&lt;/p&gt;
&lt;p&gt;Or I can give my coding agent good material about the architecture, failure modes, boundaries, tests, and decisions that matter, then let it inspect the application that already exists. I still need to understand the material, review the implementation, and have enough experience to notice when the result is wrong. I just do not need to manually reconstruct somebody else’s sample app first.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;hello-world-is-fine&quot;&gt;Hello World is fine&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The title needs an asterisk. Short tutorials and documentation are not going away. Sometimes I want to know how a method works, see one small example, or remind myself how a Rails API works after not touching it for six months.&lt;/p&gt;
&lt;p&gt;If I want to know how &lt;code dir=&quot;auto&quot;&gt;broadcast_replace_to&lt;/code&gt; works, a concise example is great. If I want to understand an N+1 query, I do not need an agentic implementation framework and a 40-page architecture document.&lt;/p&gt;
&lt;p&gt;Hello World is fine.&lt;/p&gt;
&lt;p&gt;The format starts to break down once the problem looks like a real production application. The less pristine the real app is, the less useful a pristine sample app becomes.&lt;/p&gt;
&lt;p&gt;Authentication is not hard because nobody knows how to store a password digest. Billing is not hard because nobody knows how to make a Stripe API request, and webhooks are not hard because nobody knows how to accept a &lt;code dir=&quot;auto&quot;&gt;POST&lt;/code&gt; request. The difficult part is making those things fit the rest of the application without creating three new definitions of account ownership, two billing truths, and a retry path that only works when nothing actually fails.&lt;/p&gt;
&lt;p&gt;That is repo-specific work, and the coding agent is already in the repo.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;this-is-not-really-about-vibe-coding&quot;&gt;This is not really about vibe coding&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The easy version of this argument is about beginners: somebody who barely knows how to code opens an AI tool, asks for a feature, and lets the model build the whole thing. Sure, that exists. I think the more interesting change is what happens to experienced developers.&lt;/p&gt;
&lt;p&gt;I have been building software for a long time. I usually do not need another person to explain what a background job is, and I do not need to watch them type every controller action just to understand the architecture they settled on. What I want is the expensive part of their experience.&lt;/p&gt;
&lt;p&gt;Why did you put the boundary there, and what failed before you ended up with this shape? Which assumptions actually matter? Where does concurrency become a problem? What looks like a harmless shortcut until production traffic shows up, what should the tests prove, and what should I absolutely not let the agent “clean up” because it will quietly change the behavior?&lt;/p&gt;
&lt;p&gt;That is the useful material. I do not want your sample app nearly as much as I want your decisions. Once I have those decisions, a coding agent can do a lot of the mechanical adaptation inside the application I am already working on.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;beginners-need-the-expert-context-even-more&quot;&gt;Beginners need the expert context even more&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;There is a weird assumption in some AI discussions that if models can generate code, beginner-focused education becomes less important. I think the opposite problem shows up pretty quickly.&lt;/p&gt;
&lt;p&gt;An experienced developer can look at an AI-generated implementation and feel that something is off before they can always explain why. A beginner often cannot, because they do not have enough bad deployments, race conditions, security mistakes, and regrettable abstractions behind them yet.&lt;/p&gt;
&lt;p&gt;The agent gives them far more execution ability than they would have had a few years ago. It does not automatically give them judgment.&lt;/p&gt;
&lt;p&gt;Agentic coding also lets somebody get surprisingly far while learning only enough to keep the agent moving. The old way was slower and often tedious, but manually connecting authentication, authorization, billing, jobs, tenancy, and everything else forced you to build at least some mental model of how those pieces fit together.&lt;/p&gt;
&lt;p&gt;Now the agent can connect a lot of those pieces before the developer really understands why the boundaries are where they are. That does not make the resulting architecture wrong, but it makes architectural judgment easier to skip. Teaching the implementation is not enough if the developer never learns how to tell whether a locally reasonable change still makes sense for the system as a whole.&lt;/p&gt;
&lt;p&gt;Good educational material needs to help the human understand the decisions while giving the agent enough constraints that it does not invent the important parts from scratch.&lt;/p&gt;
&lt;p&gt;The experienced developer uses that context to save time. The newer developer uses it to borrow judgment they have not built yet. Same source material, different reason for needing it.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;developer-education-needs-an-execution-layer&quot;&gt;Developer education needs an execution layer&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This is where I think the product itself has to change. Adding a chapter called “Prompts,” putting a chatbot beside a video course, or generating an AI summary of a book keeps the old product intact and bolts an AI feature onto the side.&lt;/p&gt;
&lt;p&gt;I think implementation-oriented education increasingly needs an &lt;strong&gt;execution layer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The human still needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the mental model&lt;/li&gt;
&lt;li&gt;the tradeoffs&lt;/li&gt;
&lt;li&gt;the architecture&lt;/li&gt;
&lt;li&gt;the reasoning&lt;/li&gt;
&lt;li&gt;the failure modes&lt;/li&gt;
&lt;li&gt;the vocabulary needed to review the result&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The coding agent needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;repository inspection instructions&lt;/li&gt;
&lt;li&gt;implementation constraints&lt;/li&gt;
&lt;li&gt;architectural contracts&lt;/li&gt;
&lt;li&gt;recipes that can be adapted instead of copied blindly&lt;/li&gt;
&lt;li&gt;invariants and verification steps&lt;/li&gt;
&lt;li&gt;review questions&lt;/li&gt;
&lt;li&gt;known failure modes&lt;/li&gt;
&lt;li&gt;explicit boundaries around what should not change&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The educational product should not stop at the point where the reader understands what should be built. It should be able to make the next jump into the environment where the thing is actually being built.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;i-backed-into-this-while-trying-to-make-my-own-products-less-stale&quot;&gt;I backed into this while trying to make my own products less stale&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I did not start with a grand theory about replacing technical education. I was trying to make the things I already sell fit the way I actually build software now.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://robrace.dev/webhooks-in-rails/&quot;&gt;Webhooks in Rails&lt;/a&gt; has a human-readable implementation guide, but it also includes an Agent Companion. The guide explains why the boundaries exist, while the companion gives a coding agent contracts, recipes, examples, and review questions it can use while working inside an existing application.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://railsbaseline.com&quot;&gt;Rails Baseline&lt;/a&gt; goes further. The application itself establishes patterns for authentication, authorization, tenancy, entitlements, jobs, and the other boring-but-important pieces. Its agentic material helps a coding agent discover and continue those decisions instead of re-deciding them every time a new feature is added.&lt;/p&gt;
&lt;p&gt;I did not add those pieces because “AI-powered” looks good on a landing page. I added them because handing somebody a static implementation and saying “now go adapt this to your app” started feeling incomplete.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;static-implementation-steps-are-becoming-a-commodity&quot;&gt;Static implementation steps are becoming a commodity&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The business part is uglier. A tutorial whose primary value is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Here are the steps required to implement X.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;is now competing with a machine that can generate those steps on demand while looking at the actual application.&lt;/p&gt;
&lt;p&gt;That is a rough thing to compete with. It can answer follow-up questions, explain the same thing differently, change the answer after inspecting the repository, and then start doing the work. Recording the same CRUD flow with a better microphone is not going to fix that.&lt;/p&gt;
&lt;p&gt;The valuable part moves toward the things that are harder to regenerate from a generic prompt: judgment, coherent architecture, tested constraints, strong opinions, known tradeoffs, and context that has been maintained by somebody who has actually dealt with the problem.&lt;/p&gt;
&lt;p&gt;The economics around free educational content get awkward too. For years, the basic trade was understandable: publish useful material, build an audience, and some percentage of that audience buys the deeper product.&lt;/p&gt;
&lt;p&gt;Now a developer can get a useful AI-assisted answer without necessarily visiting the source, joining the list, or buying anything. The knowledge can remain useful while the distribution model around the knowledge gets worse.&lt;/p&gt;
&lt;p&gt;Chris’s GoRails numbers are a particularly loud example, but I do not need his business to make the point. I can see the same pressure in my own sales and in my own development habits. I am consuming more technical information than ever while becoming less interested in consuming it in the old format.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-educators-job-gets-better-not-smaller&quot;&gt;The educator’s job gets better, not smaller&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;There is a pessimistic version of this where AI replaces the teacher because the model can generate endless explanations and code. I do not buy that version, because the parts of technical education I value most were never the typing. They were the decisions behind the typing.&lt;/p&gt;
&lt;p&gt;A good educator has already made the mistake I am about to make. They have already tried the abstraction that looked clean and became annoying six months later, figured out which framework convention is worth following and which one stops helping in this particular case, and seen which production failure should change the design instead of becoming one more rescue block.&lt;/p&gt;
&lt;p&gt;That is useful context for a human, and it is also extremely useful context for an agent. The job starts looking less like “record every keystroke” and more like encoding judgment well enough that both the developer and the coding agent can use it.&lt;/p&gt;
&lt;p&gt;That seems like a better product to me.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;but-i-still-read-tutorials&quot;&gt;“But I still read tutorials”&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;So do I. I still read articles to understand concepts, look up APIs, and buy books from people whose thinking I want more of.&lt;/p&gt;
&lt;p&gt;What I am much less willing to do is spend hours manually reproducing an implementation in a disposable sample app before I can apply the idea to my own code.&lt;/p&gt;
&lt;p&gt;That is what I mean by dead here. The tutorial is not gone. It just is not the whole workflow anymore.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;the-tutorial-is-being-split-into-two-things&quot;&gt;The tutorial is being split into two things&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Traditional implementation tutorials bundled two jobs together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Teach me what good looks like.&lt;/li&gt;
&lt;li&gt;Show me enough code that I can reproduce it myself.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Coding agents pull those jobs apart. The human still needs understanding, while the agent can handle much more of the translation and execution.&lt;/p&gt;
&lt;p&gt;The useful loop starts looking like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;expert knowledge&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;developer + coding agent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;actual codebase&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That changes what is worth packaging and selling.&lt;/p&gt;
&lt;p&gt;Books can still have chapters. Courses can still have video. Tutorials can still have code samples. But for implementation-heavy material, I think the complete product increasingly includes something designed to travel with the developer into their coding environment.&lt;/p&gt;
&lt;p&gt;Not a magic prompt or “build this for me,” but actual context: decisions, constraints, examples, checks, and a process for applying them to a codebase the author has never seen.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;programming-tutorials-are-dead&quot;&gt;Programming tutorials are dead&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The title is supposed to be annoying. There will still be tutorials tomorrow.&lt;/p&gt;
&lt;p&gt;What I am calling dead is the assumption that a static walkthrough of somebody else’s implementation is enough for a lot of real application work. Agents can inspect the destination codebase, carry context into the implementation, run the tests, and keep working after the tutorial would traditionally have ended.&lt;/p&gt;
&lt;p&gt;The expertise still matters. I just do not want it to stop at a sample app anymore.&lt;/p&gt;
&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>Coding Agents</category></item><item><title>Your Webhook Endpoint Is a Tiny Distributed System</title><link>https://robrace.dev/blog/your-webhook-endpoint-is-a-tiny-distributed-system/</link><guid isPermaLink="true">https://robrace.dev/blog/your-webhook-endpoint-is-a-tiny-distributed-system/</guid><description>A webhook starts as one HTTP endpoint. Making it trustworthy, durable, retryable, and safe under concurrency turns it into something much more interesting.</description><pubDate>Wed, 09 Sep 2026 07:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Webhook endpoints always seem simple when you build the first version.&lt;/p&gt;
&lt;p&gt;Add a route, create a controller action, parse some JSON, update a record and return &lt;code dir=&quot;auto&quot;&gt;200&lt;/code&gt;. Pretty standard Rails stuff.&lt;/p&gt;
&lt;p&gt;Then the real requirements start showing up. You need to verify that Stripe or GitHub actually sent the request. The provider wants a response quickly, so the useful work moves into a background job. The same event arrives twice. A worker dies after doing half of the work. Two related events get processed at the same time. Another event shows up out of order.&lt;/p&gt;
&lt;p&gt;At some point, that little controller action has picked up a surprising amount of infrastructure around it.&lt;/p&gt;
&lt;p&gt;Nobody starts by saying, “I need a distributed system for this webhook.” You normally get there one completely reasonable requirement at a time.&lt;/p&gt;
&lt;p&gt;It is still a small system, of course. We are not building Kafka and twelve services here. But once a webhook is production-ready, you have an external trust boundary, durable ingress, asynchronous workers, duplicate delivery, retries, concurrency, ordering problems and a handful of failure states that all need to agree with each other.&lt;/p&gt;
&lt;p&gt;That is the part of webhooks I find interesting. A tiny HTTP endpoint becomes a pretty good microcosm of a much larger distributed system.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;first-can-you-trust-the-request&quot;&gt;First, can you trust the request?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;A webhook is public ingress into your application, so before doing anything useful with the payload, you need to answer the obvious security question: &lt;strong&gt;did the provider actually send this?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most providers solve this with a shared secret and a signature over the request body. GitHub, for example, sends an HMAC-SHA256 signature in &lt;code dir=&quot;auto&quot;&gt;X-Hub-Signature-256&lt;/code&gt;. Stripe signs its payload and includes a timestamp in &lt;code dir=&quot;auto&quot;&gt;Stripe-Signature&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The important detail is that those signatures are based on the &lt;strong&gt;raw request body&lt;/strong&gt;, not a Ruby hash after Rails has parsed it.&lt;/p&gt;
&lt;p&gt;So the order here matters:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the exact request body.&lt;/li&gt;
&lt;li&gt;Verify the signature.&lt;/li&gt;
&lt;li&gt;Parse the payload only after you trust it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Rails gives us &lt;a href=&quot;https://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-raw_post&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;request.raw_post&lt;/code&gt;&lt;/a&gt; for the first part.&lt;/p&gt;
&lt;p&gt;A minimal GitHub controller can look something like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Webhooks::GithubController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;skip_forgery_protection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;raw_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;raw_post&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; head &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;unauthorized&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;valid_signature?(raw_body)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;payload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(raw_body)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Persist and dispatch the verified event here.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;head &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;accepted&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;rescue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;ParserError&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;head &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;bad_request&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;valid_signature?&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;raw_body&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;secret&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;credentials&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dig&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;webhook_secret&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;expected&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sha256=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;OpenSSL&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;HMAC&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;hexdigest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;OpenSSL&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Digest&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sha256&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;secret,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;raw_body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;provided&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;X-Hub-Signature-256&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;to_s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;ActiveSupport&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;SecurityUtils&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;secure_compare&lt;/span&gt;&lt;span&gt;(expected, provided)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;There are only a few lines here, but each one matters. The signature is compared with Rails’ &lt;code dir=&quot;auto&quot;&gt;secure_compare&lt;/code&gt;, the body is not parsed until after verification, and the endpoint skips normal browser CSRF protection because this request is not coming from one of our application’s browser sessions. The provider signature is what authenticates this request.&lt;/p&gt;
&lt;p&gt;For Stripe snapshot events, I would use the SDK instead of rebuilding its signature format myself:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;raw_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;raw_post&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;signature&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Stripe-Signature&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;secret&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;credentials&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;dig&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;stripe&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;webhook_secret&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;event&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Stripe&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Webhook&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;construct_event&lt;/span&gt;&lt;span&gt;(raw_body, signature, secret)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Stripe’s verifier also validates the signed timestamp against its tolerance, which helps with replay protection. Stripe’s docs are also very specific about passing the unmodified request body to the verifier.&lt;/p&gt;
&lt;p&gt;Stripe’s newer thin events use a different SDK entry point, so this example is specifically for the snapshot Event objects used by the rest of this article.&lt;/p&gt;
&lt;p&gt;So even before we process the event, the webhook endpoint already has its own trust boundary and its own security rules.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;receiving-and-processing-are-two-different-jobs&quot;&gt;Receiving and processing are two different jobs&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Ok, now that we trust the request, the next problem is how long we keep the provider waiting.&lt;/p&gt;
&lt;p&gt;The webhook provider is making a normal HTTP request to our application. It does not want to wait while we update a bunch of records, call another API, generate something, send email or do whatever the event actually triggers.&lt;/p&gt;
&lt;p&gt;GitHub.com expects a &lt;code dir=&quot;auto&quot;&gt;2xx&lt;/code&gt; response within 10 seconds. Stripe’s guidance is to return a successful &lt;code dir=&quot;auto&quot;&gt;2xx&lt;/code&gt; before doing complex processing that could time out.&lt;/p&gt;
&lt;p&gt;That leads to a fairly boring ingress path, which is exactly what I want:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;verify&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;validate enough to identify the event&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;persist a durable receipt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;enqueue work&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;return 2xx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Personally, I do not want the request itself provisioning an account, recalculating billing, synchronizing a repository or doing some other multi-step business process. The request should establish that I trust the event and that my application has accepted responsibility for it.&lt;/p&gt;
&lt;p&gt;New Rails 8 applications use Solid Queue as the default Active Job backend in production. A dispatch job can stay very normal Rails code:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebhookDispatchJob&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationJob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;queue_as &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;webhooks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;perform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;delivery_id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;delivery&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebhookDelivery&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt;(delivery_id)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Dispatch to provider/event-specific handling.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That gets the slow work out of the HTTP request, but just throwing &lt;code dir=&quot;auto&quot;&gt;perform_later&lt;/code&gt; into the controller is not the whole reliability story.&lt;/p&gt;
&lt;p&gt;If you return a &lt;code dir=&quot;auto&quot;&gt;2xx&lt;/code&gt; and then discover that the event never made it into a durable place, the provider thinks you have the event and you may not actually have it anymore.&lt;/p&gt;
&lt;p&gt;This is where the acknowledgement boundary becomes important.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-does-2xx-actually-mean&quot;&gt;What does &lt;code dir=&quot;auto&quot;&gt;2xx&lt;/code&gt; actually mean?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;I like storing a durable webhook receipt in the application database before telling the provider the event was accepted.&lt;/p&gt;
&lt;p&gt;The first version does not need to be complicated:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateWebhookDeliveries&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActiveRecord::Migration&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;8.0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;create_table &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;webhook_deliveries&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;external_id&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;event_type&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;payload&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;datetime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;processed_at&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;datetime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;failed_at&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;last_error&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;timestamps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_index &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;webhook_deliveries&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;external_id&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;unique&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With that in place, GitHub ingress can start looking more like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;raw_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;raw_post&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; head &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;unauthorized&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unless&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;valid_signature?(raw_body)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;delivery_id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;X-GitHub-Delivery&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;presence&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;event_type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;headers&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;X-GitHub-Event&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;presence&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; head &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;bad_request&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unless&lt;/span&gt;&lt;span&gt; delivery_id &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; event_type&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;payload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;(raw_body)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;delivery&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebhookDelivery&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create_or_find_by!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;provider&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;github&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;external_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; delivery_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;record&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;record.&lt;/span&gt;&lt;span&gt;event_type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; event_type&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;record.&lt;/span&gt;&lt;span&gt;payload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; payload&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;WebhookDispatchJob&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;perform_later&lt;/span&gt;&lt;span&gt;(delivery.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;head &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;accepted&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rescue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;ParserError&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;head &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;bad_request&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;One thing to call out here: the unique database index is doing real work. An &lt;code dir=&quot;auto&quot;&gt;exists?&lt;/code&gt; check followed by &lt;code dir=&quot;auto&quot;&gt;create!&lt;/code&gt; still has a race between those two queries. Two concurrent requests can both pass the check. The unique &lt;code dir=&quot;auto&quot;&gt;(provider, external_id)&lt;/code&gt; constraint is the final guard that prevents two receipt rows for the same provider event.&lt;/p&gt;
&lt;p&gt;The receipt also gives us somewhere to recover from the slightly awkward gap between inserting application data and enqueuing a job.&lt;/p&gt;
&lt;p&gt;Solid Queue has an important detail here. In a default Rails 8 production setup, Solid Queue is configured on a separate database from the application’s primary database. Creating a &lt;code dir=&quot;auto&quot;&gt;WebhookDelivery&lt;/code&gt; in one database and inserting a Solid Queue job in another is &lt;strong&gt;not one atomic transaction&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In the controller above, &lt;code dir=&quot;auto&quot;&gt;create_or_find_by!&lt;/code&gt; has already finished its transaction before &lt;code dir=&quot;auto&quot;&gt;perform_later&lt;/code&gt; runs, so the receipt is committed before enqueueing starts. If receipt creation and enqueueing happen inside a larger application transaction, Rails gives Active Job &lt;code dir=&quot;auto&quot;&gt;enqueue_after_transaction_commit&lt;/code&gt; to defer the enqueue until that transaction commits:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebhookDispatchJob&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationJob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;queue_as &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;webhooks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;enqueue_after_transaction_commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That prevents work from being enqueued for a transaction that later rolls back. It still does not create one atomic transaction across the application and queue databases.&lt;/p&gt;
&lt;p&gt;If the receipt commits and the queue insertion fails, I still want that event to be recoverable. A recurring reconciliation job that looks for old, unprocessed receipts is a simple solution and gives you another chance to enqueue the work.&lt;/p&gt;
&lt;p&gt;Now &lt;code dir=&quot;auto&quot;&gt;2xx&lt;/code&gt; has a much more useful meaning. It does not mean all of the business logic finished. It means the application has durably accepted the event and can recover the remaining work if something goes wrong.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;duplicate-delivery-is-normal&quot;&gt;Duplicate delivery is normal&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;This is probably the first webhook problem that surprises people when they have only worked with the happy path.&lt;/p&gt;
&lt;p&gt;Imagine this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stripe sends an event.&lt;/li&gt;
&lt;li&gt;Your application processes it successfully.&lt;/li&gt;
&lt;li&gt;The response gets lost or arrives too late.&lt;/li&gt;
&lt;li&gt;Stripe does not know the work succeeded.&lt;/li&gt;
&lt;li&gt;Stripe sends the event again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That is not some wild production edge case. Two systems communicated over a network and disagreed about whether the operation finished.&lt;/p&gt;
&lt;p&gt;Stripe explicitly documents that the same event can be delivered more than once. GitHub gives every delivery an &lt;code dir=&quot;auto&quot;&gt;X-GitHub-Delivery&lt;/code&gt; GUID and keeps that same GUID when a delivery is redelivered.&lt;/p&gt;
&lt;p&gt;This is why the &lt;code dir=&quot;auto&quot;&gt;(provider, external_id)&lt;/code&gt; unique index is useful. It gives us &lt;strong&gt;delivery idempotency&lt;/strong&gt;. Receiving the same provider event over and over does not create a new logical event in our application every time.&lt;/p&gt;
&lt;p&gt;However, delivery idempotency is not the same as &lt;strong&gt;business idempotency&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose two different webhook events can both lead to the same business operation. Or suppose a job calls an external API successfully and dies before it records &lt;code dir=&quot;auto&quot;&gt;processed_at&lt;/code&gt;. The unique webhook receipt does not stop that external operation from happening again on the retry.&lt;/p&gt;
&lt;p&gt;For those cases, I like the idempotency boundary to describe the actual operation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreateProvisioningOperations&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActiveRecord::Migration&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;8.0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;create_table &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;provisioning_operations&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;references&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;account&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;foreign_key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;operation_key&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;null&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;datetime&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;completed_at&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;timestamps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_index &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;provisioning_operations&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;account_id&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;operation_key&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;unique&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That &lt;code dir=&quot;auto&quot;&gt;operation_key&lt;/code&gt; could be based on a provider object ID and the action you are performing. If the next external service supports its own idempotency keys, I would use those too when making that request.&lt;/p&gt;
&lt;p&gt;This is also why I try not to think in terms of “exactly once” webhook processing. We do not control exactly how many times a provider attempts delivery, how many times a job is retried or whether a worker dies at a convenient point.&lt;/p&gt;
&lt;p&gt;What we can control is whether doing the work again is safe.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;idempotency-does-not-remove-concurrency&quot;&gt;Idempotency does not remove concurrency&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;There is another small wrinkle in the controller example above.&lt;/p&gt;
&lt;p&gt;Two copies of the same event can both resolve to the same &lt;code dir=&quot;auto&quot;&gt;WebhookDelivery&lt;/code&gt;, and both requests can enqueue &lt;code dir=&quot;auto&quot;&gt;WebhookDispatchJob&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the job starts with this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; delivery.&lt;/span&gt;&lt;span&gt;processed_at?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;…that helps with a later retry, but it does not protect against two workers starting at the same time. Both workers can read &lt;code dir=&quot;auto&quot;&gt;processed_at == nil&lt;/code&gt; before either one updates it.&lt;/p&gt;
&lt;p&gt;For work that stays entirely inside our database, a row lock can make that state transition explicit:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebhookDispatchJob&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationJob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;queue_as &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;webhooks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;perform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;delivery_id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;delivery&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebhookDelivery&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;span&gt;(delivery_id)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;delivery.&lt;/span&gt;&lt;span&gt;with_lock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; delivery.&lt;/span&gt;&lt;span&gt;processed_at?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;Webhooks&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Github&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Dispatch&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;call&lt;/span&gt;&lt;span&gt;(delivery)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;delivery.&lt;/span&gt;&lt;span&gt;update!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;processed_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Time&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;current&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That example assumes &lt;code dir=&quot;auto&quot;&gt;Dispatch&lt;/code&gt; is doing database work that belongs inside the transaction.&lt;/p&gt;
&lt;p&gt;I would &lt;strong&gt;not&lt;/strong&gt; keep a row lock open while making a slow request to another service. Once the handler has external side effects, that business operation should normally have its own durable idempotency protection instead of assuming a database lock in Rails can control what another system does.&lt;/p&gt;
&lt;p&gt;So now our tiny webhook endpoint has a concurrency model too. Fun!&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;there-are-two-retry-systems&quot;&gt;There are two retry systems&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once processing moves to Solid Queue, there are two completely separate retry loops to think about: the provider retrying webhook delivery and our application retrying the async processing.&lt;/p&gt;
&lt;p&gt;For Stripe, the flow looks roughly like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Stripe&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓ webhook delivery&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails ingress&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓ durable receipt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Solid Queue&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓ processing attempt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;business state&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If Rails does not return a successful response, Stripe can retry the webhook delivery for up to three days in live mode using exponential backoff.&lt;/p&gt;
&lt;p&gt;Once Rails has accepted the event, the job can fail independently. Active Job does not automatically retry every failed job for you; retry behavior is something you configure. A transient network failure could use something like:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WebhookDispatchJob&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationJob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;queue_as &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;webhooks&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;retry_on &lt;/span&gt;&lt;span&gt;Net&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;OpenTimeout&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Net&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;ReadTimeout&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;wait&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;polynomially_longer&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;attempts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;perform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;delivery_id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;I would not put every exception behind one giant retry rule. A timeout to another API may be temporary. A malformed payload is not. A missing mapping or a bug in the handler probably needs a fix rather than five delayed attempts at the exact same broken code.&lt;/p&gt;
&lt;p&gt;Provider behavior is not universal here either. GitHub does &lt;strong&gt;not&lt;/strong&gt; automatically redeliver a failed webhook delivery. You can redeliver manually or through the API, but that is very different from Stripe retrying failed deliveries on its own.&lt;/p&gt;
&lt;p&gt;That is another reason I like owning the durable receipt and having a reconciliation path inside the Rails application. Recovery should not depend entirely on whatever retry policy the provider happens to use.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;and-then-events-arrive-out-of-order&quot;&gt;And then events arrive out of order&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Even after making every individual event safe to retry, you can still get the wrong result if your code assumes events arrive in the order they happened.&lt;/p&gt;
&lt;p&gt;Stripe does not guarantee event ordering. GitHub also documents that webhook deliveries can arrive in a different order than the underlying events occurred.&lt;/p&gt;
&lt;p&gt;A billing flow could end up looking like this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;subscription.updated&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;subscription.deleted&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;subscription.updated   # delayed older event&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If each handler blindly writes its payload to the local record, that delayed event can move the local state backward.&lt;/p&gt;
&lt;p&gt;The fix depends on the provider. Sometimes there is a useful sequence or version. Be careful with timestamps: Stripe’s snapshot Event &lt;code dir=&quot;auto&quot;&gt;created&lt;/code&gt; value only has second-level precision, and Stripe says not to use it to determine event order.&lt;/p&gt;
&lt;p&gt;For important state, another option is to treat the webhook as a notification that something changed and then fetch the provider’s current canonical object before making a local decision. That read can fail when the object has been deleted or is no longer available, and it is not an ordering guarantee by itself. Concurrent handlers still need a provider-specific version or sequence when one exists, local serialization, or a reconciliation rule.&lt;/p&gt;
&lt;p&gt;The main point is to make the ordering assumption explicit. “This is the webhook I am processing now” does not automatically mean “this is the newest state.”&lt;/p&gt;
&lt;p&gt;At this point we have duplicate messages, retries, concurrent workers and events showing up out of order. The distributed-system comparison is doing a lot less metaphorical work than it did at the beginning.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;every-provider-has-its-own-little-rules&quot;&gt;Every provider has its own little rules&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;There is definitely common webhook infrastructure worth sharing, but I would be careful about trying to make Stripe, GitHub, Shopify, Twilio and every other provider look identical too early.&lt;/p&gt;
&lt;p&gt;Verification happens before trust. Durable receipts can share a lifecycle. Jobs can share dispatch infrastructure. Logging and admin tooling can be normalized.&lt;/p&gt;
&lt;p&gt;The provider details still matter.&lt;/p&gt;
&lt;p&gt;Stripe and GitHub alone are enough to show the differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Signatures:&lt;/strong&gt; Stripe uses a timestamped &lt;code dir=&quot;auto&quot;&gt;Stripe-Signature&lt;/code&gt; format and provides an SDK verifier. GitHub uses &lt;code dir=&quot;auto&quot;&gt;X-Hub-Signature-256&lt;/code&gt; with HMAC-SHA256.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retry behavior:&lt;/strong&gt; Stripe automatically retries failed live deliveries for up to three days. GitHub does not automatically redeliver failures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ordering:&lt;/strong&gt; both document that deliveries can arrive out of order.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delivery identity:&lt;/strong&gt; Stripe events have event IDs. GitHub provides a delivery GUID in &lt;code dir=&quot;auto&quot;&gt;X-GitHub-Delivery&lt;/code&gt; that stays the same across redelivery.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Response expectations:&lt;/strong&gt; GitHub expects a &lt;code dir=&quot;auto&quot;&gt;2xx&lt;/code&gt; within 10 seconds. Stripe recommends returning a &lt;code dir=&quot;auto&quot;&gt;2xx&lt;/code&gt; quickly before complex processing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I normally prefer a common ingress shape with provider-specific verification and dispatch over a generic abstraction that hides details the application actually needs to know.&lt;/p&gt;
&lt;p&gt;In Rails, this can stay pretty conventional:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Webhooks::GithubController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;GithubSignatureVerifier&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;WebhookDelivery&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;WebhookDispatchJob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Webhooks::Github::Dispatch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;provider/event-specific handler&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;There does not need to be a framework behind every box. The useful part is having an obvious place for each responsibility so that Stripe-specific logic does not slowly leak into GitHub processing and vice versa.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;a-200-is-not-observability&quot;&gt;A &lt;code dir=&quot;auto&quot;&gt;200&lt;/code&gt; is not observability&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Once the useful work is asynchronous, the HTTP request stops telling us very much about what eventually happened.&lt;/p&gt;
&lt;p&gt;A successful webhook response can mean the signature passed, a receipt was stored and the work was enqueued while absolutely none of the actual business processing has happened yet.&lt;/p&gt;
&lt;p&gt;Eventually you need to answer questions about the event lifecycle instead of the HTTP request.&lt;/p&gt;
&lt;p&gt;Did we receive delivery &lt;code dir=&quot;auto&quot;&gt;abc123&lt;/code&gt;? Was it a duplicate? What event type was it? Did processing ever finish? How many times did it fail? What was the last error? Can I replay it after fixing the bug?&lt;/p&gt;
&lt;p&gt;This is another place where the &lt;code dir=&quot;auto&quot;&gt;WebhookDelivery&lt;/code&gt; model earns its keep. It gives logs, admin tooling, reconciliation and manual replay a stable record to work from.&lt;/p&gt;
&lt;p&gt;I usually want enough state to distinguish something along these lines:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;received → processing → processed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↘ failed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You do not need to build a workflow engine for this. A few timestamps, an error field and a clear job boundary may be plenty. But once processing can happen seconds or minutes after the request, “the endpoint returned 200” is not a useful debugging answer anymore.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;ok-so-we-built-a-tiny-distributed-system&quot;&gt;Ok, so we built a tiny distributed system&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Let’s look back at everything our original controller picked up along the way.&lt;/p&gt;
&lt;p&gt;We now have an untrusted network boundary, cryptographic verification, a durable message receipt, database uniqueness, an asynchronous queue, concurrent workers, duplicate delivery, an internal retry policy, a provider retry policy, out-of-order events, business idempotency, failure state, reconciliation, replay and observability.&lt;/p&gt;
&lt;p&gt;None of those are especially exotic problems by themselves. The interesting part is how many of them get packed into a feature that started as &lt;code dir=&quot;auto&quot;&gt;POST /webhooks/stripe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This does &lt;strong&gt;not&lt;/strong&gt; mean the first webhook endpoint needs fifteen classes and a homegrown framework. I would still start pretty small. I just want the important boundaries to be obvious:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;POST /webhooks/:provider&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;verify the raw payload&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;persist one durable receipt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;enqueue Solid Queue work&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;provider-specific handler&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;idempotent business operation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;record the outcome&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;From there, add complexity when a real failure mode calls for it.&lt;/p&gt;
&lt;p&gt;The controller can stay boring. That is probably the best outcome.&lt;/p&gt;
&lt;p&gt;The complicated part was never parsing the JSON. It was accepting work from another system when you do not control how many times the message arrives, when it arrives, what order it arrives in or which process eventually finishes it.&lt;/p&gt;
&lt;p&gt;That is a lot hiding behind one Rails controller action.&lt;/p&gt;
&lt;aside aria-label=&quot;Note&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Note&lt;/p&gt;&lt;div&gt;&lt;p&gt;If you want the Rails implementation version of this, &lt;a href=&quot;https://robrace.dev/webhooks-in-rails/&quot;&gt;Webhooks in Rails&lt;/a&gt; goes deeper on verification, durable receipt, idempotency, retries, jobs, testing, and provider-specific behavior, and includes an Agent Companion for repo-aware coding agents.&lt;/p&gt;&lt;p&gt;Already have webhook code in an existing Rails application? The &lt;a href=&quot;https://robrace.dev/webhooks-in-rails/checkup/&quot;&gt;free Webhook Architecture Checkup&lt;/a&gt; is a repo-aware prompt for tracing the flow you already have and finding the important gaps.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>Rails</category><category>Webhooks</category><category>Architecture</category></item><item><title>I Tested Rails Baseline by Leaving Architecture Out of the Prompt</title><link>https://robrace.dev/blog/i-tested-rails-baseline-by-leaving-architecture-out-of-the-prompt/</link><guid isPermaLink="true">https://robrace.dev/blog/i-tested-rails-baseline-by-leaving-architecture-out-of-the-prompt/</guid><description>What two fresh coding-agent sessions revealed about an agent-friendly Rails starter.</description><pubDate>Thu, 27 Aug 2026 07:00:00 GMT</pubDate><content:encoded>&lt;aside aria-label=&quot;Note&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Note&lt;/p&gt;&lt;div&gt;&lt;p&gt;&lt;a href=&quot;https://railsbaseline.com&quot;&gt;Rails Baseline&lt;/a&gt; is the production-ready Rails foundation I tested here. It gives a new SaaS application—and the coding agents working in it—clear, conventional patterns to build from.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;I did something a little different before shipping Rails Baseline 1.0.&lt;/p&gt;
&lt;p&gt;Instead of spending another week adding starter features I thought somebody might need, I took the actual packaged ZIP, opened it as if I had just bought it, and handed it to fresh coding-agent sessions.&lt;/p&gt;
&lt;p&gt;Then I gave them product prompts.&lt;/p&gt;
&lt;p&gt;I did not tell them to use Pundit. I did not say to scope records through the current account, use public IDs, put paid access behind Entitlements, or use Solid Queue for background work. Those were all patterns already in the repository, and I wanted to see if the agent would find them without the prompt turning into an architecture checklist.&lt;/p&gt;
&lt;p&gt;One test used Claude. The other used Codex.&lt;/p&gt;
&lt;p&gt;Both went pretty well.&lt;/p&gt;
&lt;p&gt;That sentence is intentionally boring. I was not trying to figure out which model could win a Rails benchmark. I wanted to test the codebase.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;why-test-the-starter-this-way&quot;&gt;Why test the starter this way?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Rails Baseline is a normal Rails application, but part of the reason I built it was for coding agents.&lt;/p&gt;
&lt;p&gt;I have spent years building SaaS apps and eventually wrote &lt;em&gt;Build A SaaS App in Ruby on Rails 8&lt;/em&gt;. A lot of the hard-won stuff from doing that is not a particular controller action or gem install. It is knowing where certain decisions belong and trying not to make a different version of the same decision six months later.&lt;/p&gt;
&lt;p&gt;Coding agents make this more obvious because they can produce so much code so quickly.&lt;/p&gt;
&lt;p&gt;If the repository has a good pattern, the agent can usually find and continue it. If the repository is silent, the agent still has to build something. At that point it either asks, guesses, or follows whatever general Rails pattern seems reasonable from training.&lt;/p&gt;
&lt;p&gt;A starter that claims to work well with agents should probably be tested by giving an agent incomplete product instructions and seeing what happens.&lt;/p&gt;
&lt;p&gt;So that is what I did.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;test-one-build-a-feedback-board&quot;&gt;Test one: build a feedback board&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The first test started from the RC1 release ZIP, outside the Rails Baseline source repository. I onboarded it as a new application and gave a fresh Claude session a prompt to build a small feedback board product.&lt;/p&gt;
&lt;p&gt;The basic product was pretty normal SaaS stuff. Accounts could create boards, anonymous visitors could leave feedback, feedback had statuses, and plan limits changed how many boards an account could have. Paid accounts also got a CSV export.&lt;/p&gt;
&lt;p&gt;I intentionally described those things as product requirements.&lt;/p&gt;
&lt;p&gt;I did not say something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code dir=&quot;auto&quot;&gt;Current.account&lt;/code&gt; for tenancy.&lt;/li&gt;
&lt;li&gt;Use Pundit for authorization.&lt;/li&gt;
&lt;li&gt;Use &lt;code dir=&quot;auto&quot;&gt;Entitlements.enabled?&lt;/code&gt; for paid capabilities.&lt;/li&gt;
&lt;li&gt;Use &lt;code dir=&quot;auto&quot;&gt;Limits.value&lt;/code&gt; for board counts.&lt;/li&gt;
&lt;li&gt;Use &lt;code dir=&quot;auto&quot;&gt;HasPublicId&lt;/code&gt; for URLs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That would prove I can write a detailed prompt. I already know I can do that.&lt;/p&gt;
&lt;p&gt;The more interesting question was whether Claude would inspect the app and reach those conclusions itself.&lt;/p&gt;
&lt;p&gt;It mostly did.&lt;/p&gt;
&lt;p&gt;The board records were account-owned. Authorization followed the existing policy shape. The free and paid board counts went through the application’s plan-limit boundary instead of being scattered through controllers. The paid CSV feature used the entitlement layer. Public URLs followed the existing public-ID convention.&lt;/p&gt;
&lt;p&gt;There was also a policy-scope mistake during the build. Claude caught it and fixed it during its own pass.&lt;/p&gt;
&lt;p&gt;Good. That is much closer to what I actually want from an agent session than a first pass that looks impressive but quietly breaks account isolation.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;the-useful-miss&quot;&gt;The useful miss&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The CSV export exposed something Rails Baseline had not really answered yet.&lt;/p&gt;
&lt;p&gt;Where should a generated file live?&lt;/p&gt;
&lt;p&gt;It would have been easy to react by adding Active Storage, S3 setup, an Export model, and some generic file-delivery service to the starter. That felt like exactly the kind of thing I did not want Baseline to become.&lt;/p&gt;
&lt;p&gt;A lot of products do need durable generated files. Plenty do not. A CSV that can be generated quickly and downloaded immediately may not need to be stored at all.&lt;/p&gt;
&lt;p&gt;So the answer became documentation instead of another core subsystem. I added a generated-artifacts recipe that talks through when synchronous generation is enough and when durable storage becomes a product decision.&lt;/p&gt;
&lt;p&gt;That gave me something else to test later.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;test-two-build-an-endpoint-monitor&quot;&gt;Test two: build an endpoint monitor&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The second test started from RC2 and used a fresh Codex session.&lt;/p&gt;
&lt;p&gt;This prompt was more demanding. Build an account-owned endpoint monitor that checks public HTTP/HTTPS URLs on a schedule. Free accounts get three monitors checked every 15 minutes. Paid accounts get 25 monitors checked every five minutes. Keep recent check history, support a paid CSV export, and optionally expose a public status page.&lt;/p&gt;
&lt;p&gt;Again, the prompt described the product. I did not name the Rails Baseline implementation pieces I expected it to discover.&lt;/p&gt;
&lt;p&gt;Codex found most of them.&lt;/p&gt;
&lt;p&gt;It used account scoping and the existing Pundit context. It used persisted public IDs, Entitlements, Limits, tenant-aware jobs, and the Solid Queue recurring configuration. For the CSV export, Codex found the generated-artifacts recipe from the first dogfood and chose not to invent persistent storage for a file that could be generated on demand.&lt;/p&gt;
&lt;p&gt;That last part made me pretty happy. The first test found a missing decision. I documented it, and a completely fresh agent used that documentation in the second test.&lt;/p&gt;
&lt;p&gt;That is basically the behavior I wanted from Rails Baseline in the first place.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;the-second-app-found-harder-gaps&quot;&gt;The second app found harder gaps&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;An endpoint monitor has one security problem that a feedback board does not have: the application has to make HTTP requests to URLs supplied by users.&lt;/p&gt;
&lt;p&gt;Codex correctly treated that as an SSRF problem instead of just checking whether the string started with &lt;code dir=&quot;auto&quot;&gt;https://&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Its implementation resolved DNS, rejected private and other unsafe address ranges, pinned the connection to the validated address, used explicit timeouts, verified TLS, disabled environment proxies, and did not follow redirects.&lt;/p&gt;
&lt;p&gt;Rails Baseline did not teach any of that.&lt;/p&gt;
&lt;p&gt;I do not really consider that a failure of the starter. Safe outbound HTTP is one of those capabilities that only some products need, and there are enough security details that “just use &lt;code dir=&quot;auto&quot;&gt;Net::HTTP&lt;/code&gt;” is not much of a recipe.&lt;/p&gt;
&lt;p&gt;Still, the test found a real boundary where an agent had to leave the existing precedent and design something new. That became one of the small recipes added before 1.0.&lt;/p&gt;
&lt;p&gt;The scheduling side found another one.&lt;/p&gt;
&lt;p&gt;Baseline already showed recurring Solid Queue work and tenant context inside jobs, but it did not show the shape needed when each account’s schedule depends on product state. Codex ended up with a minute-level dispatcher that finds monitors which are due and enqueues their actual check jobs.&lt;/p&gt;
&lt;p&gt;That also became a short recipe instead of a new scheduling framework.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;and-yes-the-agent-still-wrote-a-bug&quot;&gt;And, yes, the agent still wrote a bug&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The endpoint monitor app ended up calling the model &lt;code dir=&quot;auto&quot;&gt;HttpMonitor&lt;/code&gt; because &lt;code dir=&quot;auto&quot;&gt;Monitor&lt;/code&gt; collides with a Ruby constant and &lt;code dir=&quot;auto&quot;&gt;EndpointMonitor&lt;/code&gt; collided with the onboarded application namespace.&lt;/p&gt;
&lt;p&gt;The routes still used the product-facing name:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;monitors&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;At one point, a view used Rails’ polymorphic routing shortcut:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; link_to monitor.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;, monitor &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Rails saw an &lt;code dir=&quot;auto&quot;&gt;HttpMonitor&lt;/code&gt; instance and looked for &lt;code dir=&quot;auto&quot;&gt;http_monitor_path&lt;/code&gt;, which did not exist. The route helper was &lt;code dir=&quot;auto&quot;&gt;monitor_path&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That one made it through the agent’s test suite, and I hit it manually while clicking around the app.&lt;/p&gt;
&lt;p&gt;The fix was easy, but I liked finding this too. A coding agent following the architecture well does not mean it stops making normal implementation mistakes. It also gave Baseline one more small Rails-specific recipe: if your model class and route resource intentionally use different names, either avoid polymorphic routing there or test a navigation path that exercises it.&lt;/p&gt;
&lt;p&gt;No framework required.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-i-actually-learned-from-the-tests&quot;&gt;What I actually learned from the tests&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The useful result was not “Claude passed” or “Codex passed.”&lt;/p&gt;
&lt;p&gt;Both agents were capable enough that the interesting differences came from what the repository gave them to work with.&lt;/p&gt;
&lt;p&gt;When Rails Baseline had a clear local pattern, the agents usually found it. Account ownership, authorization, plan access, limits, jobs, IDs, and normal Rails/Hotwire UI conventions all carried through into new product code without me naming those pieces in the prompt.&lt;/p&gt;
&lt;p&gt;When the app did not have a pattern, the agent had to make a product decision. Sometimes that was exactly right. Other times it exposed a missing piece of documentation that was general enough to add back to the starter.&lt;/p&gt;
&lt;p&gt;That seems like a healthier way to build an agent-friendly codebase than trying to anticipate every future feature with another abstraction.&lt;/p&gt;
&lt;p&gt;I would rather have 20 boring, well-used patterns and a handful of good recipes than 80 generic service objects waiting for a use case.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;i-will-probably-keep-doing-this&quot;&gt;I will probably keep doing this&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The best part of the process was how close it felt to an actual customer using the product.&lt;/p&gt;
&lt;p&gt;I was not running tests inside the Baseline source repo and congratulating myself because they were green. The ZIP was packaged, copied somewhere else, onboarded into a new app, and then handed to an agent that had no history from building Rails Baseline.&lt;/p&gt;
&lt;p&gt;That caught setup issues, product gaps, documentation gaps, and one wonderfully ordinary Rails routing bug.&lt;/p&gt;
&lt;p&gt;For future Baseline releases, I think this kind of test is more valuable than adding another sample feature just so the feature list gets longer.&lt;/p&gt;
&lt;p&gt;Give a fresh agent a product request. See what it copies. See where it guesses. Fix the parts that should have been obvious from the repository.&lt;/p&gt;
&lt;p&gt;Then stop before the starter turns into the product it is supposed to help you build.&lt;/p&gt;
&lt;p&gt;If you want to see the Rails app these tests were run against, Rails Baseline is at &lt;a href=&quot;https://railsbaseline.com&quot;&gt;railsbaseline.com&lt;/a&gt;.&lt;/p&gt;
&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>Rails</category><category>Coding Agents</category><category>SaaS</category></item><item><title>Print Smarter in Ruby - Know When and How to Use Each Method</title><link>https://robrace.dev/blog/printing-in-ruby/</link><guid isPermaLink="true">https://robrace.dev/blog/printing-in-ruby/</guid><description>A quick tour through the most common ways to print in Ruby</description><pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve ever stared at a blob of console output wondering what the hell you just printed, you’re not alone. Ruby gives you a handful of ways to print stuff, but knowing &lt;em&gt;when&lt;/em&gt; to use which one is the real trick. In this post, we’ll go through the five most common ways to print output in Ruby, plus one gem that makes your console look like it’s had a glow-up.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;puts-standard-output-with-automatic-newlines&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt;: Standard Output with Automatic Newlines&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; is your go-to for dumping simple output to the console. It stands for “put string” and adds a newline at the end of each argument. It’s as friendly and low-drama as it sounds:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Hello, World!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;123&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Ruby&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Hello, World!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;123&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Ruby&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Arrays get printed line-by-line, so don’t be surprised when each item shows up on its own line.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;print-continuous-output-without-newlines&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;print&lt;/code&gt;: Continuous Output Without Newlines&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Want to print multiple things on the same line? Use &lt;code dir=&quot;auto&quot;&gt;print&lt;/code&gt;. It’s like &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt;, but without the automatic newlines:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Hello, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;World!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Hello, World!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you want a newline, you’ll have to add it manually:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Hello, World!&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;p-debugging-with-inspect-output&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;p&lt;/code&gt;: Debugging with Inspect Output&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;p&lt;/code&gt; is the debug sibling of &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt;. It shows the &lt;code dir=&quot;auto&quot;&gt;inspect&lt;/code&gt; version of the object and means strings keep their quotes, and you get more detail:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Hello, World!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;39&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Hello, World!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;39&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you’re debugging and want raw visibility into what you’re working with, &lt;code dir=&quot;auto&quot;&gt;p&lt;/code&gt; is your friend.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;pp-pretty-print-for-enhanced-readability&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;pp&lt;/code&gt;: Pretty Print for Enhanced Readability&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;For more complex stuff like nested hashes, &lt;code dir=&quot;auto&quot;&gt;pp&lt;/code&gt; (pretty print) makes things readable:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;pp&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;complex_hash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;hobbies&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;coding&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fitness&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;span&gt;sports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cycling&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lifting&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]}]}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pp complex_hash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;hobbies&lt;/span&gt;&lt;span&gt;=&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;coding&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fitness&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;sports&lt;/span&gt;&lt;span&gt;=&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cycling&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lifting&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]}]}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Think of it like &lt;code dir=&quot;auto&quot;&gt;p&lt;/code&gt;, but with spatial awareness.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;y-yaml-output-in-irb&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;y&lt;/code&gt;: YAML Output in IRB&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;y&lt;/code&gt; method is only available in IRB (Interactive Ruby), but it’s a great way to spit out YAML-formatted data without much effort:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# In IRB&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;languages&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Ruby&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;JavaScript&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;y data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;:name: Rob&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;:languages:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- Ruby&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;- JavaScript&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It’s tidy, structured, and way more readable when you’re dealing with nested stuff.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;quick-summary&quot;&gt;Quick Summary&lt;/h2&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;General output, automatic newline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;print&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Continuous output without newline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Debugging with inspect details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;pp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pretty printing for readability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code dir=&quot;auto&quot;&gt;y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;YAML-formatted output (IRB only)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If you just want something quick and readable, &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;p&lt;/code&gt; will get you far. But if you’re working with nested hashes from an API and don’t want to burn your eyes out reading the raw dump then read on.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;awesome_print-enhanced-pretty-printing-with-style&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;awesome_print&lt;/code&gt;: Enhanced Pretty Printing with Style&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If &lt;code dir=&quot;auto&quot;&gt;pp&lt;/code&gt; gets the job done, &lt;code dir=&quot;auto&quot;&gt;awesome_print&lt;/code&gt; gets the job done &lt;em&gt;with flair&lt;/em&gt;. It pretty-prints Ruby objects in full color, indents them beautifully, and makes your console output feel like VS Code decided to move in.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Using Bundler:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;group &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;development&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gem &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;awesome_print&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then run:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Or install it directly:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;awesome_print&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;/h3&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;awesome_print&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;hobbies&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;coding&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fitness&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;sports&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;cycling&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lifting&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;] }]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ap data&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;:name =&gt; &quot;Rob&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;:hobbies =&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[0] &quot;coding&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[1] &quot;fitness&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[2] {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;:sports =&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[0] &quot;cycling&quot;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[1] &quot;lifting&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To make it the default in IRB:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;awesome_print&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;AwesomePrint&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;irb!&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once you’ve used &lt;code dir=&quot;auto&quot;&gt;awesome_print&lt;/code&gt;, going back to &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; feels like reading minified JSON with no line breaks.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That’s your tour of Ruby’s printing options. Use &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;p&lt;/code&gt; when you’re starting out. Graduate to &lt;code dir=&quot;auto&quot;&gt;pp&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;awesome_print&lt;/code&gt; when your objects get gnarly. And next time you’re knee-deep in nested hashes wondering why nothing looks right, don’t forget your options.&lt;/p&gt;
&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>Ruby</category></item><item><title>Avoiding N+1 Queries in Rails: Easy Performance Wins for Beginners</title><link>https://robrace.dev/blog/n1-in-rails/</link><guid isPermaLink="true">https://robrace.dev/blog/n1-in-rails/</guid><description>Learn how to spot and fix N+1 query problems in your Rails app with practical examples, tools, and simple performance tips.</description><pubDate>Wed, 14 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Performance issues in Rails apps can sneak up on you, especially if you’re working with ActiveRecord and unaware of the infamous N+1 query problem. This post will walk you through what N+1 queries are, how to detect them, and how to fix them using simple techniques. If you’re a beginner or intermediate Rails developer, this is one of the easiest ways to level up your performance game.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;what-is-an-n1-query&quot;&gt;What is an N+1 Query?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Imagine going to the store with a shopping list of 10 items and making a separate trip for each one instead of buying them all at once. That’s what an N+1 query does: it performs 1 query to fetch a list of records (say &lt;code dir=&quot;auto&quot;&gt;Post.all&lt;/code&gt;), and then 1 additional query for each associated record (like &lt;code dir=&quot;auto&quot;&gt;post.comments&lt;/code&gt;).&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# N+1 problem example&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;comments&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This triggers one query to get all posts, and then one query per post to fetch its comments. If you have 10 posts, that’s 11 queries. Scale that up to 100 posts, and you’re dealing with 101 queries, most of them unnecessary.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;how-to-detect-n1-queries&quot;&gt;How to Detect N+1 Queries&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;1-check-your-logs&quot;&gt;1. Check Your Logs&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;In development, check your &lt;code dir=&quot;auto&quot;&gt;log/development.log&lt;/code&gt; or Rails server output. You’ll often see the same query being run repeatedly.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;2-use-the-bullet-gem&quot;&gt;2. Use the &lt;code dir=&quot;auto&quot;&gt;bullet&lt;/code&gt; Gem&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/flyerhzm/bullet&quot;&gt;bullet gem&lt;/a&gt; is designed to catch N+1 queries and notify you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Gemfile&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;group &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;development&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gem &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;bullet&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Configuration:&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;config/environments/development.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;config.&lt;/span&gt;&lt;span&gt;after_initialize&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Bullet&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;enable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Bullet&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Bullet&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;bullet_logger&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Bullet&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;rails_logger&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You’ll get an alert in your browser or console when Bullet detects a potential N+1.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;how-to-fix-n1-queries&quot;&gt;How to Fix N+1 Queries&lt;/h2&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;use-eager-loading-with-includes&quot;&gt;Use Eager Loading with &lt;code dir=&quot;auto&quot;&gt;.includes&lt;/code&gt;&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Eager loading tells Rails to fetch associated records in the same query using &lt;code dir=&quot;auto&quot;&gt;LEFT OUTER JOIN&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Fixing the N+1 problem&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;comments&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;comments&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, instead of 11 queries for 10 posts, you only make &lt;strong&gt;2&lt;/strong&gt;: one for the posts, and one for the associated comments.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;when-to-use-joins-or-preload&quot;&gt;When to Use &lt;code dir=&quot;auto&quot;&gt;.joins&lt;/code&gt; or &lt;code dir=&quot;auto&quot;&gt;.preload&lt;/code&gt;&lt;/h3&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code dir=&quot;auto&quot;&gt;.joins&lt;/code&gt; when you’re filtering or ordering based on associated data.&lt;/li&gt;
&lt;li&gt;Use &lt;code dir=&quot;auto&quot;&gt;.preload&lt;/code&gt; when you want to load associations but don’t need joins.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;common-mistakes-and-gotchas&quot;&gt;Common Mistakes and Gotchas&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Forgetting to use eager loading in controllers or views.&lt;/li&gt;
&lt;li&gt;Not eager loading nested associations (&lt;code dir=&quot;auto&quot;&gt;post.comments.user&lt;/code&gt; requires &lt;code dir=&quot;auto&quot;&gt;Post.includes(comments: :user)&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Overusing &lt;code dir=&quot;auto&quot;&gt;.includes&lt;/code&gt; can also hurt performance if you load too much data.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;h2 id=&quot;real-world-example&quot;&gt;Real-World Example&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Say you’re building a blog app and rendering a list of posts with their comments:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;comments&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;comment&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; comment.&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If &lt;code dir=&quot;auto&quot;&gt;@posts = Post.all&lt;/code&gt;, you’re triggering N+1 queries. Instead, use:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;comments&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This change alone can dramatically cut down your query count and improve performance.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;preventing-n1s-going-forward&quot;&gt;Preventing N+1s Going Forward&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Use the &lt;code dir=&quot;auto&quot;&gt;bullet&lt;/code&gt; gem in development to get real-time alerts.&lt;/li&gt;
&lt;li&gt;Build a habit of checking logs when building or refactoring features.&lt;/li&gt;
&lt;li&gt;Write performance tests if your app grows in size or complexity.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Want to keep improving your Rails performance? There’s a performance chapter in my &lt;a href=&quot;https://buildasaasappinrails.com&quot;&gt;Build A SaaS App in Ruby on Rails 8 book&lt;/a&gt; that covers this and more.&lt;/p&gt;</content:encoded><category>Rails</category></item><item><title>How to Build a Twitter Clone with Rails 8 Inertia and React</title><link>https://robrace.dev/blog/build-a-twitter-clone-with-rails-inertia-and-react/</link><guid isPermaLink="true">https://robrace.dev/blog/build-a-twitter-clone-with-rails-inertia-and-react/</guid><description>An update to How to Build a Twitter Clone with Rails and React</description><pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While I have written a bunch lately about using the “Rails way” to build applications, I have been keeping an eye on InertiaJS via internet friends, especially those in the Laravel space.&lt;/p&gt;
&lt;p&gt;InertiaJS is a way to build single-page applications (SPAs) without creating an API. Specifically, using the &lt;code dir=&quot;auto&quot;&gt;inertia-rails&lt;/code&gt; gem, you can easily make a Rails application that uses InertiaJS to add frontend libraries.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails new blabber --main --css=bootstrap&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Tip&lt;/p&gt;&lt;div&gt;&lt;p&gt;If you like using Tailwind, you can install it with InertiaJS’s install process. However, to maintain continuity with the other clone tutorials, we’ll stick with Bootstrap.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Rails will do its thing: install the application, install the gems, and process the Bootstrap and dependencies installs.&lt;/p&gt;
&lt;p&gt;Next, we can review the model, controller, and routing setup before proceeding to the InertiaJS and React components.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails g model Post username body:text likes_count:integer repost_count:integer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To keep this closely resembling the formerly bird app that we are cloning, we’ll add the same validation in the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; model:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;minimum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;280&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We’ll make a few small adjustments to the generated migration file to add some database-level defaults (and allows us to keep the code around &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; creation simple):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreatePosts&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActiveRecord::Migration&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;6.0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;create_table &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Blabby&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;timestamps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Ok! Now, we’re ready to run that migration!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Before going forward with the controller, now that you have the database and model set, we should install the &lt;code dir=&quot;auto&quot;&gt;inertia-rails&lt;/code&gt; gem as it will dictate some differences in the controller and view layer (through frontend components).:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gem &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;inertia_rails&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the gem installed, we can run the installer:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bundle install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails generate inertia:install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The installer is interactive and will ask a few questions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do you want to install Vite Ruby? This library is the build tool for handling the front-end assets through InertiaJS. I selected yes, and I assume if you choose no, you would already be running some other front-end build process.&lt;/li&gt;
&lt;li&gt;Would you like to use TypeScript? I use TypeScript in my projects, so I selected yes. If you are not familiar with TypeScript, you can choose no.&lt;/li&gt;
&lt;li&gt;What framework do you want to use with Inertia? [react, vue, svelte4, svelte] I selected React, as that is what we are using in this tutorial.&lt;/li&gt;
&lt;li&gt;Would you like to use Tailwind CSS? I selected no, as we are using Bootstrap in this tutorial.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The installer will install all those dependencies and set up the Rails application to use InertiaJS. It will also create a demo controller and “Page” component to show you how to use InertiaJS. Those files are found at &lt;code dir=&quot;auto&quot;&gt;app/controllers/inertia_example_controller.rb&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;app/javascript/pages/InertiaExample.tsx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With everything added, you should be able to run &lt;code dir=&quot;auto&quot;&gt;bin/dev&lt;/code&gt; and see the Rails server and Vite server running. You can visit &lt;code dir=&quot;auto&quot;&gt;http://localhost:3000/inertia_example&lt;/code&gt; to see the demo page.&lt;/p&gt;
&lt;p&gt;With the inertiaJS install out of the way, we can move on to the controller and corresponding view templates!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PostsController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;render &lt;/span&gt;&lt;span&gt;inertia&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;as_json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(post_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to new_post_path, &lt;/span&gt;&lt;span&gt;inertia&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post_params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;params.&lt;/span&gt;&lt;span&gt;expect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Simple controller. The &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; action returns a list of posts to &lt;code dir=&quot;auto&quot;&gt;@post&lt;/code&gt; and renders a &lt;code dir=&quot;auto&quot;&gt;Posts&lt;/code&gt; “page” in the front-end, which we will add shortly. &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; creates a new Post and redirects back to the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; component or the form if it fails. InertiaJS handles the routing in the front end to ensure the correct React rendering is happening. &lt;code dir=&quot;auto&quot;&gt;like&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;repost&lt;/code&gt; are similar, except they increment the respective count columns.&lt;/p&gt;
&lt;p&gt;Let’s wire up a few routes to match up to those controller actions. Yes, these aren’t perfect RESTful routes, but 1) They work. 2) This is a 10-minute tutorial.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;inertia-example&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;inertia_example#index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Can be used by load balancers and uptime monitors to verify that the app is live.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;rails/health#show&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;rails_health_check&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# get &quot;manifest&quot; =&gt; &quot;rails/pwa#manifest&quot;, as: :pwa_manifest&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# get &quot;service-worker&quot; =&gt; &quot;rails/pwa#service_worker&quot;, as: :pwa_service_worker&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Defines the root path route (&quot;/&quot;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; %i[&lt;/span&gt;&lt;span&gt;index create&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;posts#index&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The inertia install modified the &lt;code dir=&quot;auto&quot;&gt;application.html.erb&lt;/code&gt; file to include the &lt;code dir=&quot;auto&quot;&gt;Inertia&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;Vite&lt;/code&gt; tags. To make sure everything is copasetic, here is the entire file:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;inertia&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content_for(:&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;width=device-width,initial-scale=1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;apple-mobile-web-app-capable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;yes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mobile-web-app-capable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;yes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csrf_meta_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csp_meta_tag &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%#= tag.link rel: &quot;manifest&quot;, href: pwa_manifest_path(format: :json) %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;image/png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.svg&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;image/svg+xml&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;apple-touch-icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%# Includes all stylesheet files in app/assets/stylesheets %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; stylesheet_link_tag &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data-turbo-track&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; javascript_importmap_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; stylesheet_link_tag &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data-turbo-track&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; vite_react_refresh_tag &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; vite_client_tag &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; vite_typescript_tag &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inertia&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; inertia_ssr_head &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; vite_javascript_tag &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;!--&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;If using a TypeScript entrypoint file:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vite_typescript_tag &apos;application&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;If using a .jsx or .tsx entrypoint, add the extension:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;vite_javascript_tag &apos;application.jsx&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Visit the guide for more information: https://vite-ruby.netlify.app/guide/rails&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;--&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; console &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Tip&lt;/p&gt;&lt;div&gt;&lt;p&gt;I was having trouble with the &lt;code dir=&quot;auto&quot;&gt;vite&lt;/code&gt; hot module reloading until I added the key-value &lt;code dir=&quot;auto&quot;&gt;&quot;host&quot;: &quot;127.0.0.1&quot;&lt;/code&gt; to the “development” object in the &lt;code dir=&quot;auto&quot;&gt;vite.json&lt;/code&gt; file in the &lt;code dir=&quot;auto&quot;&gt;config&lt;/code&gt; directory in the app. From what I have read about diagnosing my issue, this problem seems like a known issue with &lt;code dir=&quot;auto&quot;&gt;vite&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;localhost&lt;/code&gt;&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This covers most of the initial changes needed on the traditional Rails side, and we can now start adding the React components.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Head } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@inertiajs/react&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { PostForm } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;../components/PostForm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { PostList } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;../components/PostList&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Post } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;../types/Post&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Posts&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { posts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;[] }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Head&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Posts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A Rails, Inertia, React demo!&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;PostForm&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;PostList&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A great thing about using React is the composability of the components that you would typically not get with ERB templates. The &lt;code dir=&quot;auto&quot;&gt;Posts&lt;/code&gt; component is a wrapper for the &lt;code dir=&quot;auto&quot;&gt;PostForm&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;PostList&lt;/code&gt; components. You may also notice that we’re pulling in a &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; type to the &lt;code dir=&quot;auto&quot;&gt;PostList&lt;/code&gt; component. This import is a TypeScript type that we will define in a moment. Lastly, as mentioned before, inertiaJS will handle routing and rendering of these components. Thus, they provide a &lt;code dir=&quot;auto&quot;&gt;Head&lt;/code&gt; component for the page’s title.&lt;/p&gt;
&lt;p&gt;For quick reference, here is the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; type:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/javascript/types/Post.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;interface&lt;/span&gt;&lt;span&gt; Post {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;PostForm&lt;/code&gt; component is a simple form that will allow the user to create a new post:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/javascript/components/PostForm.tsx&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useForm } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@inertiajs/react&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export const &lt;/span&gt;&lt;span&gt;PostForm&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;const { &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;processing&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;reset&lt;/span&gt;&lt;span&gt; } = &lt;/span&gt;&lt;span&gt;useForm&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body: &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;function &lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;React&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&lt;span&gt;FormEvent&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;preventDefault&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/posts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;onSuccess&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;reset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;my-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onSubmit&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mb-3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;textarea&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-control&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;rows&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Enter Your Blab&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;onChange&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;setData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mb-3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btn btn-primary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;disabled&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;processing&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blab&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;InertiaJS does a lot of the heavy lifting for you, including form handling. The &lt;code dir=&quot;auto&quot;&gt;useForm&lt;/code&gt; hook is a custom hook that InertiaJS provides to handle form data, submission, and errors. The &lt;code dir=&quot;auto&quot;&gt;submit&lt;/code&gt; function is a simple function that will post the form data to the &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; action in the &lt;code dir=&quot;auto&quot;&gt;PostsController&lt;/code&gt;. The form is a straightforward Bootstrap form with a textarea and a submit button.&lt;/p&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;PostList&lt;/code&gt; component is a simple list of posts:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/javascript/components/PostList.tsx&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useEffect, useState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;react&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Post } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;../types/Post&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { PostItem } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;../components/PostItem&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export const &lt;/span&gt;&lt;span&gt;PostList&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;PostItem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;PostList&lt;/code&gt; is relatively small component as it maps over the &lt;code dir=&quot;auto&quot;&gt;posts&lt;/code&gt; prop and renders a &lt;code dir=&quot;auto&quot;&gt;PostItem&lt;/code&gt; component for each post.&lt;/p&gt;
&lt;p&gt;Next up is the aforementioned &lt;code dir=&quot;auto&quot;&gt;PostItem&lt;/code&gt; component:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/javascript/components/PostItem.tsx&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Post } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;../types/Post&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Link } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@inertiajs/react&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export const &lt;/span&gt;&lt;span&gt;PostItem&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;post_&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-title text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Posted at &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-text lead mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;/posts/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/repost&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Repost (&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;/posts/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/like&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Like (&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;Link&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;PostItem&lt;/code&gt; component is a simple Bootstrap card that displays the post’s username, body, and creation date. It also includes two links to repost and like the post. The links are InertiaJS &lt;code dir=&quot;auto&quot;&gt;Link&lt;/code&gt; components that will handle the routing and rendering of the repost and like actions.&lt;/p&gt;
&lt;p&gt;Alright! That is all the React components we need to build the clone. If you head to your Rails app, you should be able to see the posts form and a list of posts. You can create a new post, which will appear in the list. Plus, you can like and repost posts.&lt;/p&gt;
&lt;p&gt;However, if you have followed along with any of the other clone tutorials, you know that we are missing one key feature: real-time updates. In this tutorial, we will use &lt;code dir=&quot;auto&quot;&gt;ActionCable&lt;/code&gt; to add real-time updates and a npm package called &lt;code dir=&quot;auto&quot;&gt;use-action-cable&lt;/code&gt; to handle the front-end subscription.&lt;/p&gt;
&lt;p&gt;First, we can add the ActionCable side to get the backend ready for the front-end subscription. We’ll start by adding a new channel to handle the posts (which will, in turn, add a few new default files as well):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails g channel PostsChannel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next, we’ll add the &lt;code dir=&quot;auto&quot;&gt;subscribed&lt;/code&gt; method to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt; class to stream from the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/channels/posts_channel.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationCable::Channel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stream_from &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unsubscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Any cleanup needed when channel is unsubscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Also, we’ll edit the &lt;code dir=&quot;auto&quot;&gt;Connection&lt;/code&gt; file to identify the Cable connection with the browser session ID:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt; ApplicationCable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Connection&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActionCable::Connection::Base&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;identified_by &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;session_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Lastly, for the backend, we’ll add a broadcast to the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; model to broadcast the post to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/models/post.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;minimum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;280&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;after_commit &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;broadcast&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;destroy&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;broadcast&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ActionCable&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;broadcast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;as_json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This change will broadcast the posts to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt; after they are created, updated, or destroyed. Yes, this is ultra simplistic by returning all of the posts, but it will work for this tutorial. You would want to scope this down in a production application.&lt;/p&gt;
&lt;p&gt;Alright, that’s it for the ActionCable backend! Next up is the &lt;code dir=&quot;auto&quot;&gt;React&lt;/code&gt; side of the application.&lt;/p&gt;
&lt;p&gt;We will use a more current fork of the original &lt;code dir=&quot;auto&quot;&gt;use-action-cable&lt;/code&gt; package. This package will allow us to subscribe to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt; and handle the incoming posts.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn add react-use-action-cable-ts&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This package utilizes a few custom hooks to add a connection to an ActionCable channel easily. We’ll use the &lt;code dir=&quot;auto&quot;&gt;useActionCable&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;useChannel&lt;/code&gt; hooks to subscribe to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt; and handle the incoming posts.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/javascript/pages/Posts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useEffect, useState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;react&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Post } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;../types/Post&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useActionCable, useChannel } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;react-use-action-cable-ts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { PostItem } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;../components/PostItem&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export const &lt;/span&gt;&lt;span&gt;PostList&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;const [&lt;/span&gt;&lt;span&gt;postData&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setPostData&lt;/span&gt;&lt;span&gt;] = &lt;/span&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;const { &lt;/span&gt;&lt;span&gt;actionCable&lt;/span&gt;&lt;span&gt; } = &lt;/span&gt;&lt;span&gt;useActionCable&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;ws://127.0.0.1:3000/cable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;const { &lt;/span&gt;&lt;span&gt;subscribe&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;unsubscribe&lt;/span&gt;&lt;span&gt; } = &lt;/span&gt;&lt;span&gt;useChannel&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;actionCable&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;receiveCamelCase: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;verbose: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;sendSnakeCase: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;useEffect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;subscribe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;channel: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;received&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;const &lt;/span&gt;&lt;span&gt;cableData&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; as { &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;[]&lt;/span&gt;&lt;span&gt; };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;setPostData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cableData&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;return &lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;unsubscribe&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;},&lt;/span&gt;&lt;span&gt; [])&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;postData&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;PostItem&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The changes here will start with adding &lt;code dir=&quot;auto&quot;&gt;useState&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;useEffect&lt;/code&gt; to the &lt;code dir=&quot;auto&quot;&gt;PostList&lt;/code&gt; component. These hooks will allow us to set the incoming posts from the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt; broadcast. We’ll also add the &lt;code dir=&quot;auto&quot;&gt;useActionCable&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;useChannel&lt;/code&gt; hooks to handle connecting to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt;. We will connect to &lt;code dir=&quot;auto&quot;&gt;&quot;ws://127.0.0.1:3000/cable&quot;&lt;/code&gt; to access the local ActionCable server output. Still, you should utilize another method of getting the current host if you put this code in Production. The hook returns an &lt;code dir=&quot;auto&quot;&gt;actionCable&lt;/code&gt; object that we can use to subscribe to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;useChannel&lt;/code&gt; hook will use that &lt;code dir=&quot;auto&quot;&gt;actionCable&lt;/code&gt; object to set up a function to subscribe to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt; and set a few settings for the channel. Specifically, the &lt;code dir=&quot;auto&quot;&gt;receiveCamelCase&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;sendSnakeCase&lt;/code&gt; settings are set to &lt;code dir=&quot;auto&quot;&gt;false&lt;/code&gt; to match the Rails backend. The &lt;code dir=&quot;auto&quot;&gt;verbose&lt;/code&gt; setting is set to &lt;code dir=&quot;auto&quot;&gt;false&lt;/code&gt; to reduce the console output.&lt;/p&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;useEffect&lt;/code&gt; React hook will take the &lt;code dir=&quot;auto&quot;&gt;subscribe&lt;/code&gt; function from the &lt;code dir=&quot;auto&quot;&gt;useChannel&lt;/code&gt; hook and subscribe to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt;. The &lt;code dir=&quot;auto&quot;&gt;subscribe&lt;/code&gt; function takes two arguments: the channel name and an object with a &lt;code dir=&quot;auto&quot;&gt;received&lt;/code&gt; function that will handle the incoming data. The &lt;code dir=&quot;auto&quot;&gt;received&lt;/code&gt; function will set the incoming posts to the &lt;code dir=&quot;auto&quot;&gt;postData&lt;/code&gt; state. The &lt;code dir=&quot;auto&quot;&gt;useEffect&lt;/code&gt; hook will also return an &lt;code dir=&quot;auto&quot;&gt;unsubscribe&lt;/code&gt; function that will unsubscribe from the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt; when the component is unmounted.&lt;/p&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Tip&lt;/p&gt;&lt;div&gt;&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;subscribe&lt;/code&gt; function’s second argument takes multiple functions that can handle different events. For example, you could add a &lt;code dir=&quot;auto&quot;&gt;connected&lt;/code&gt; function to handle when the connection is established or a &lt;code dir=&quot;auto&quot;&gt;disconnected&lt;/code&gt; function to handle when the connection is lost.&lt;/p&gt;&lt;p&gt;All that function will return a &lt;code dir=&quot;auto&quot;&gt;cable&lt;/code&gt; object that can be used to send messages to the &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt;. This returned object can send messages to the backend.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;With all these changes, you should have a fully functioning clone that uses InertiaJS and React for the front end and ActionCable for real-time updates. You can create, like, and repost posts, which will be updated in real-time across all clients.&lt;/p&gt;
&lt;p&gt;You can head back to &lt;a href=&quot;https://localhost:3000&quot;&gt;https://localhost:3000&lt;/a&gt; and see the posts in real-time now!&lt;/p&gt;
&lt;p&gt;There you go! I bet that with a fast enough system to work through the Rails, gem, and Javascript package installs, you could complete this tutorial in less than 10 minutes!&lt;/p&gt;
&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>React</category><category>Rails</category><category>Inertia</category><category>ActionCable</category></item><item><title>Adding Sign Up to the Rails 8 Authentication Generator</title><link>https://robrace.dev/blog/rails-8-authentication-sign-up/</link><guid isPermaLink="true">https://robrace.dev/blog/rails-8-authentication-sign-up/</guid><description>Tips for the new Rails 8 Authentication Generator</description><pubDate>Thu, 03 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This article spurned from writing about the new Rails 8 Authentication Generator in my &lt;a href=&quot;https://buildasaasappinrails.com&quot;&gt;Build A SaaS App in Ruby on Rails 8 book&lt;/a&gt;, and I wanted to share this small tip from the chapter on Users and Authentication.&lt;/p&gt;
&lt;p&gt;Within the book, I set up a Rails 8 application with authentication using the &lt;code dir=&quot;auto&quot;&gt;bin/rails generate authentication&lt;/code&gt; command. This command creates a User model, a Sessions controller, and a few views to get you started. However, it does not include a Sign-up process at all.&lt;/p&gt;
&lt;p&gt;In this quick article, we will add a Sign-Up form to the application.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Before we start, ensure you have the Rails 8 application set up with the authentication generator.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;myapp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--main&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;myapp&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;generate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Tip&lt;/p&gt;&lt;div&gt;&lt;p&gt;If you used &lt;code dir=&quot;auto&quot;&gt;--css=tailwind&lt;/code&gt;, you will get some basic but decent styling for all of this right out of the box.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;h2 id=&quot;adding-sign-up&quot;&gt;Adding Sign Up&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;To add a Sign-Up form, we will need to add a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A route to the Sign Up form&lt;/li&gt;
&lt;li&gt;A controller action to render the Sign-Up form&lt;/li&gt;
&lt;li&gt;A view to display the Sign Up form&lt;/li&gt;
&lt;li&gt;A controller action to handle the form submission&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;h3 id=&quot;route&quot;&gt;Route&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;In the &lt;code dir=&quot;auto&quot;&gt;config/routes.rb&lt;/code&gt; file, add a route to the Sign Up form in addition to the routes created by the authentication generator:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;config/routes.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resource &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resource &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;registration&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; %i[&lt;/span&gt;&lt;span&gt;new create&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;passwords&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;controller&quot;&gt;Controller&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Create a new controller called &lt;code dir=&quot;auto&quot;&gt;RegistrationsController&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/controllers/registrations_controller.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;RegistrationsController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;allow_unauthenticated_access&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(user_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;start_new_session_for &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to root_path, &lt;/span&gt;&lt;span&gt;notice&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Successfully signed up!&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user_params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;params.&lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;permit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;email_address&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;password_confirmation&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It’s pretty standard stuff here. First, &lt;code dir=&quot;auto&quot;&gt;allow_unauthenticated_access&lt;/code&gt; is a method that allows unauthenticated access to the &lt;code dir=&quot;auto&quot;&gt;new&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; actions. Otherwise, we could use this controller with a valid session. The only other thing that is not standard Rails controller CRUD is the &lt;code dir=&quot;auto&quot;&gt;start_new_session_for @user&lt;/code&gt;, which allows the app to be signed in (add session record, secure cookie, etc.) after the user is created.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;view&quot;&gt;View&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Create a new view file called &lt;code dir=&quot;auto&quot;&gt;new.html.erb&lt;/code&gt; in the &lt;code dir=&quot;auto&quot;&gt;app/views/registrations&lt;/code&gt; directory (view assumes Tailwind was added and has some classes):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/registrations/new.html.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mx-auto md:w-2/3 w-full&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt; = flash[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;py-2 px-3 bg-red-50 mb-5 text-red-500 font-medium rounded-lg inline-block&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; alert &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;notice&lt;/span&gt;&lt;span&gt; = flash[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;notice&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;notice&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; notice &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;font-bold text-4xl&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Sign up&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; form_with &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; registration_url, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;contents&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;my-5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; form.&lt;/span&gt;&lt;span&gt;email_field&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;email_address&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;autofocus&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;autocomplete&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Enter your email address&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;email_address&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;my-5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; form.&lt;/span&gt;&lt;span&gt;password_field&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;autocomplete&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;current-password&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Enter your password&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maxlength&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;72&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;my-5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; form.&lt;/span&gt;&lt;span&gt;password_field&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;password_confirmation&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;required&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;autocomplete&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;current-password&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Confirm your password&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maxlength&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;72&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;col-span-6 sm:flex sm:items-center sm:gap-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;inline&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; form.&lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Sign up&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mt-4 text-sm text-gray-500 sm:mt-0&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; link_to &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Forgot password?&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, new_password_path, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-gray-700 underline&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h3 id=&quot;form-submission&quot;&gt;Form Submission&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;If you were to try to submit the form now, it would work, but redirect to the registration form as there is no root path or authenticated path to test this all out on. Let’s create a quick controller and route to handle this:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/controllers/home_controller.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HomeController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;render &lt;/span&gt;&lt;span&gt;plain&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Welcome to the app!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;config/routes.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;home/index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;home#index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resource &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resource &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;registration&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; %i[&lt;/span&gt;&lt;span&gt;new create&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;passwords&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;param&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;token&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you visit the root path of your application, you should see the Sign-Up form. You can fill it out and submit it to create a new user. Upon successful creation, you should be redirected to the root path with the message “Welcome to the app!”&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;bonus-idea&quot;&gt;Bonus Idea&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;Usually, when building an app, there is a different layout when you are logged in than when you are on one of the “authentication adjacent” pages. You can add a method to the &lt;code dir=&quot;auto&quot;&gt;ApplicationController&lt;/code&gt; to determine if the user is authenticated or not:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/controllers/application_controller.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActionController::Base&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;before_action &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;set_layout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set_layout&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@layout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; user_signed_in? &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;authentication&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then, in your &lt;code dir=&quot;auto&quot;&gt;app/views/layouts&lt;/code&gt; directory, create two layout files: &lt;code dir=&quot;auto&quot;&gt;application.html.erb&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;authentication.html.erb&lt;/code&gt;. In the &lt;code dir=&quot;auto&quot;&gt;application.html.erb&lt;/code&gt; file, you can add a navigation bar, footer, etc. You can add a different navigation bar, footer, etc., in the &lt;code dir=&quot;auto&quot;&gt;authentication.html.erb&lt;/code&gt; file.&lt;/p&gt;
&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>Rails</category><category>Rails 8</category></item><item><title>Leveraging Turbo 8: Best Additions to Implement in Rails 8 Projects</title><link>https://robrace.dev/blog/turbo-morphs-presence-channels-and-typing-indicators/</link><guid isPermaLink="true">https://robrace.dev/blog/turbo-morphs-presence-channels-and-typing-indicators/</guid><description>Morphs, Presence, and Typing Indicators</description><pubDate>Thu, 26 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This article will explore Turbo 8’s powerful features and a little bit of code. I swear, you will be surprised by how little extra code is needed to implement all of this!&lt;/p&gt;
&lt;p&gt;We will be starting over but using many of the same concepts from the &lt;a href=&quot;https://robrace.dev/blog/build-a-twitter-clone-with-rails-8-and-turbo-8/&quot;&gt;previous article&lt;/a&gt;. Let’s get into it!&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;First, we can create a new Rails app from the &lt;code dir=&quot;auto&quot;&gt;main&lt;/code&gt; branch to get all of the Rails 8 goodies (though, soon, this will be available through alpha/beta gem releases).&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--main&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blabber&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, as we will be adding a few features related to user interactions, we will need to create users and a way to log them in. Short of using the new Rails 8 authentication generators, we can add two quick models, a small number of controller methods, and a few view templates.&lt;/p&gt;
&lt;p&gt;First, the models via Rails generators:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;email:string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;password_digest:string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user:references&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;message:text&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;These generators will create two models and migrations for the &lt;code dir=&quot;auto&quot;&gt;Users&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;Posts&lt;/code&gt;. Let’s add a few lines to the User model for validation and authentication:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;presence&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;uniqueness&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;has_secure_password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The significant addition here is the &lt;code dir=&quot;auto&quot;&gt;has_secure_password&lt;/code&gt; line which includes code to set and authenticate passwords via &lt;code dir=&quot;auto&quot;&gt;bcrypt&lt;/code&gt;. Now, on the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; model, the user reference should exist, and we can add a quick validation:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;belongs_to &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;minimum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;280&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, here is a quick &lt;code dir=&quot;auto&quot;&gt;bin/rails db:migrate&lt;/code&gt; to get those tables created.&lt;/p&gt;
&lt;p&gt;Now, we can move on to the controller and views for logging in. We want to make this simple, just for this tutorial, so we’ll handle logging in and logging out, their responsive routes, and some basic views.&lt;/p&gt;
&lt;p&gt;First, the routes:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/login&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sessions#new&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;login&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;post &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/sessions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sessions#create&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/logout&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sessions#destroy&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;logout&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; %i[&lt;/span&gt;&lt;span&gt;index create&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;posts#index&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;These will be all the routes we need for the whole app: the first three for sessions, a resource helper for &lt;code dir=&quot;auto&quot;&gt;posts&lt;/code&gt;, and setting the root route.&lt;/p&gt;
&lt;p&gt;Now we can add the &lt;code dir=&quot;auto&quot;&gt;SessionsController&lt;/code&gt; at &lt;code dir=&quot;auto&quot;&gt;app/controllers/sessions_controller.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SessionsController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params.&lt;/span&gt;&lt;span&gt;dig&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;authenticate&lt;/span&gt;&lt;span&gt;(params.&lt;/span&gt;&lt;span&gt;dig&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;sign_in(&lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to root_path, &lt;/span&gt;&lt;span&gt;notice&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;You have successfully logged in!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flash.&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;There was a problem logging in.&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;422&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;destroy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;sign_out&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to login_path, &lt;/span&gt;&lt;span&gt;notice&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;You have successfully logged out!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sign_in&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;session[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sign_out&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;session.&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Pretty simple here. If we find a user and that user authenticates with the included &lt;code dir=&quot;auto&quot;&gt;authenticate&lt;/code&gt; method from the &lt;code dir=&quot;auto&quot;&gt;User&lt;/code&gt; model, we will call &lt;code dir=&quot;auto&quot;&gt;sign_in&lt;/code&gt;, which sets a session for the user. Real quick, we can add a helper method in &lt;code dir=&quot;auto&quot;&gt;ApplicationController&lt;/code&gt; to use &lt;code dir=&quot;auto&quot;&gt;current_user&lt;/code&gt; throughout controllers and views through the app:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;current_user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@current_user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; session[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;]) &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; session[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;helper_method &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;current_user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next, we can quickly add a &lt;code dir=&quot;auto&quot;&gt;PostsController&lt;/code&gt; that resembles much of what we used in the last tutorial, aside from now having a user_id:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PostsController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;before_action &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;require_login&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(post_params.&lt;/span&gt;&lt;span&gt;merge&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;respond_to &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post_params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;params.&lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;permit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Both the &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; actions are reasonably straightforward Rails-like methods for listing and creating &lt;code dir=&quot;auto&quot;&gt;Posts&lt;/code&gt;. The last part of the authentication puzzle here would be to add the &lt;code dir=&quot;auto&quot;&gt;before_action:require_login&lt;/code&gt; method to the ApplicationController and use the Rails Console to create two users (thus, we skipped building a signup interface).&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/controllers/application_controller.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;## redirect to login, if there is no current_user, meaning there is no authenticated user session&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;require_login&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to login_path, &lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;You must be logged in to access this page.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;nil?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Lastly, we can go ahead and knock out a few views to get all of the authentication, Bootstrap, and basic &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; markup out of the way&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/layouts/application.html.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content_for(:&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;width=device-width,initial-scale=1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;apple-mobile-web-app-capable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;yes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mobile-web-app-capable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;yes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; current_user &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;current-user-id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csrf_meta_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csp_meta_tag &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_refreshes_with &lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;morph&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;scroll&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;preserve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%#= tag.link rel: &quot;manifest&quot;, href: pwa_manifest_path(format: :json) %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;image/png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.svg&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;image/svg+xml&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;apple-touch-icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stylesheet&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text/css&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%# Includes all stylesheet files in app/views/stylesheets %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; stylesheet_link_tag &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;app&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;data-turbo-track&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; javascript_importmap_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;navbar navbar-expand-lg navbar-light bg-light&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;         &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container-fluid&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;navbar-brand&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;navbar-text&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; current_user &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;               &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;               &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; link_to &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Logout&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, logout_path, &lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;delete&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btn btn-outline-danger&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;               &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; link_to &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Login&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, login_path, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btn btn-outline-primary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; flash[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;notice&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;alert alert-success&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; flash[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;notice&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; flash[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;alert alert-danger&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; flash[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;alert&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container mt-5&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A few code blocks to note:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; current_user &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;current-user-id&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;…will add the user ID to the front end for use in StimulusJS later.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_refreshes_with &lt;/span&gt;&lt;span&gt;method&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;morph&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;scroll&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;preserve&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;…will allow Turbo 8 to morph the incoming DOM changes sent by Turbo.&lt;/p&gt;
&lt;p&gt;Finally, Bootstrap is added over CDN for the simplicity of this tutorial, as well as a simple navbar and alerts.&lt;/p&gt;
&lt;p&gt;Ok, three &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; templates, and we’re done with setup.&lt;/p&gt;
&lt;p&gt;To display the list of posts, you can create an &lt;code dir=&quot;auto&quot;&gt;index.html.erb&lt;/code&gt; file in the &lt;code dir=&quot;auto&quot;&gt;app/views/posts&lt;/code&gt; directory. Here’s an example of how the index view could look like:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A Rails, Hotwire demo&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This code uses the &lt;code dir=&quot;auto&quot;&gt;_post.html.erb&lt;/code&gt; partial to render each post in the &lt;code dir=&quot;auto&quot;&gt;@posts&lt;/code&gt; collection.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dom_id(post)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-title text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Posted &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;time_ago_in_words(post.created_at)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; ago&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-text lead mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;…and then the &lt;code dir=&quot;auto&quot;&gt;_form.html.erb&lt;/code&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; form_with &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dom_id(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;my-4&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;any?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error_explanation&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pluralize(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.errors.count, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; prohibited this post from&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;being saved:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;full_messages&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; msg &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-group&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-controller&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; f.&lt;/span&gt;&lt;span&gt;text_area&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Enter your blab&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;form-control&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;rows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions my-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; f.&lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btn btn-primary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This code uses the &lt;code dir=&quot;auto&quot;&gt;form_with&lt;/code&gt; helper to create a form for the &lt;code dir=&quot;auto&quot;&gt;@post&lt;/code&gt; object. The &lt;code dir=&quot;auto&quot;&gt;text_area&lt;/code&gt; helper creates a textarea input for the post’s &lt;code dir=&quot;auto&quot;&gt;message&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;Ok! That’s enough setup code!&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;turbo-morph&quot;&gt;Turbo Morph&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you followed along with the previous tutorial, there are only a couple of lines to add to get morphed updates out of the box! Earlier, we pre-empted the code to add with the line to the partial. To finish this off, we will add a line to the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; model and two through the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; views.&lt;/p&gt;
&lt;p&gt;First, the line to the model:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;belongs_to &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;broadcasts_refreshes &lt;/span&gt;&lt;span&gt;## New line&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;minimum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;280&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/posts/index.html.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A Rails, Hotwire demo&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_stream_from &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- This is that streams from the post collection --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/posts/_post.html.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_stream_from post &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- This is that streams from each post --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dom_id(post)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-title text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Posted &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;time_ago_in_words(post.created_at)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; ago&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-text lead mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s it! If you create two users in the console, log on in, and then log another in on an incognito tab, you will see each page get updated with new posts from each user!&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;presence-channel&quot;&gt;Presence Channel&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Presence channels are a great way to show who is online in your application.&lt;/p&gt;
&lt;p&gt;In our case, we will use an indicator next to the users’ email addresses in the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; card. This indicator will be a simple green dot if the user is online and a red dot if the user is offline.&lt;/p&gt;
&lt;p&gt;The first step here will be to include icons to use a circle and then color it based on the user’s presence. We can easily include Bootstrap icons by adding the following line to the &lt;code dir=&quot;auto&quot;&gt;application.css&lt;/code&gt; file:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, we can generate a quick migration to add an &lt;code dir=&quot;auto&quot;&gt;online&lt;/code&gt; boolean to the &lt;code dir=&quot;auto&quot;&gt;User&lt;/code&gt; model:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;migration&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add_online_to_users&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;online:boolean&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next, we can add two simple methods to the &lt;code dir=&quot;auto&quot;&gt;User&lt;/code&gt; to adjust a user’s presence status using the &lt;code dir=&quot;auto&quot;&gt;online&lt;/code&gt; boolean. We will also add a method to broadcast changes to the presence channel after a user’s status changes and check for the &lt;code dir=&quot;auto&quot;&gt;online_previously_changed?&lt;/code&gt; (a built-in ActiveRecord field status method) in that method used by the callback:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/models/user.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;presence&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;uniqueness&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;has_secure_password&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;after_commit &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;broadcast_changes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;broadcast_changes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; online_previously_changed?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;Turbo&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;StreamsChannel&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;broadcast_refresh_to&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;presence&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;came_online&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;update!(&lt;/span&gt;&lt;span&gt;online&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unless&lt;/span&gt;&lt;span&gt; online?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;went_offline&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;update!(&lt;/span&gt;&lt;span&gt;online&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; online?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, we can add a line to the &lt;code dir=&quot;auto&quot;&gt;application.html.erb&lt;/code&gt; to listen for changes to the presence channel and then update the &lt;code dir=&quot;auto&quot;&gt;_post&lt;/code&gt; partial to include the presence indicator:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/layouts/application.html.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_stream_from &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;presence&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;channel&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PresenceChannel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; current_user &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;turbo_stream_from:presence&lt;/code&gt; will use the &lt;code dir=&quot;auto&quot;&gt;turbo_stream&lt;/code&gt; helpers from the &lt;code dir=&quot;auto&quot;&gt;turbo-rails&lt;/code&gt; gem to handle the automatic subscription to the presence channel. The &lt;code dir=&quot;auto&quot;&gt;PresenceChannel&lt;/code&gt; argument in this method sets which Channel will be specifically used in the streaming process.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/posts/_post.html.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_stream_from post &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dom_id(post)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-title text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Posted &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;time_ago_in_words(post.created_at)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; ago&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bi bi-circle-fill align-middle text &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;online?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;text-success&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; : &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;text-danger&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;font-size: .5rem&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-text lead mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This uses the &lt;code dir=&quot;auto&quot;&gt;bi-circle-fill&lt;/code&gt; icon from Bootstrap and changes its color based on the user’s presence status.&lt;/p&gt;
&lt;p&gt;Lastly, we can create the &lt;code dir=&quot;auto&quot;&gt;PresenceChannel&lt;/code&gt; in the &lt;code dir=&quot;auto&quot;&gt;app/channels&lt;/code&gt; directory:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/channels/presence_channel.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PresenceChannel&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActionCable::Channel::Base&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;extend&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Turbo&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Streams&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Broadcasts&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;Turbo&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Streams&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;StreamName&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Turbo&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Streams&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;StreamName&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;ClassMethods&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stream_from &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;presence&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;current_user.&lt;/span&gt;&lt;span&gt;came_online&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unsubscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;current_user.&lt;/span&gt;&lt;span&gt;went_offline&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This channel extends and includes some &lt;code dir=&quot;auto&quot;&gt;Turbo::Streams&lt;/code&gt; modules, so that it can broadcast to the presence channel and handle the stream name. The &lt;code dir=&quot;auto&quot;&gt;subscribed&lt;/code&gt; method will stream from the &lt;code dir=&quot;auto&quot;&gt;presence&lt;/code&gt; channel and then call the &lt;code dir=&quot;auto&quot;&gt;came_online&lt;/code&gt; method on the current user. The &lt;code dir=&quot;auto&quot;&gt;unsubscribed&lt;/code&gt; method will call the &lt;code dir=&quot;auto&quot;&gt;went_offline&lt;/code&gt; method on the current user. &lt;code dir=&quot;auto&quot;&gt;unsubscribe&lt;/code&gt; is a method that is called when the client disconnects from the channel by closing the window or logging out.&lt;/p&gt;
&lt;p&gt;Now, if you tried to use the browser, you would see errors from your channel in the logs, as we have to set up one last piece, the &lt;code dir=&quot;auto&quot;&gt;current_user&lt;/code&gt; in the &lt;code dir=&quot;auto&quot;&gt;Connection&lt;/code&gt; class. This class is much like an &lt;code dir=&quot;auto&quot;&gt;ApplicationController&lt;/code&gt; but for the ActionCable connections. We can add a method to the &lt;code dir=&quot;auto&quot;&gt;Connection&lt;/code&gt; class to set the &lt;code dir=&quot;auto&quot;&gt;current_user&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/channels/application_cable/connection.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt; ApplicationCable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Connection&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActionCable::Connection::Base&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;identified_by &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;current_user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;current_user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; find_verified_user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;find_verified_user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;current_user&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;current_user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;reject_unauthorized_connection&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This module will set the &lt;code dir=&quot;auto&quot;&gt;current_user&lt;/code&gt; to the user found by the &lt;code dir=&quot;auto&quot;&gt;session[:user_id]&lt;/code&gt;, which was set during the login, or reject the connection if the user is not found.&lt;/p&gt;
&lt;p&gt;If you were to open two browsers (your current browser window and an incognito would work!) now and log into each user, you would see the presence indicator change in real time!&lt;/p&gt;
&lt;aside aria-label=&quot;Note&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Note&lt;/p&gt;&lt;div&gt;&lt;p&gt;This presence could become noisy with a lot of users, specifically because it will be writing a lot to the &lt;code dir=&quot;auto&quot;&gt;users&lt;/code&gt; table. This example was built for simplicity and not scale. With enough traffic on a real application, you may want to consider a second database or technology to store the data (e.g., Redis).&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;h2 id=&quot;typing-indicators&quot;&gt;Typing Indicators&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;The last feature we’ll build here is a typing indicator, which will show when a user is typing a message, much like you see in Slack. Of course, as this is just a simple app to demonstrate the capability, the typing indicator will be shown to all users but not the user typing.&lt;/p&gt;
&lt;p&gt;First, we can generate a channel to handle the typing events and pin a debounce library to handle the typing events in a javascript controller a few files later:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;generate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;channel&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/importmap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lodash.debounce&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Inside the &lt;code dir=&quot;auto&quot;&gt;TypingChannel&lt;/code&gt;, we can add a few methods to handle the typing and typing stopped events from the “Cable” side:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/channels/typing_channel.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TypingChannel&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationCable::Channel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stream_from &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ActionCable&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;broadcast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;uid&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;to_s&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;user_email&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt; } )&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;typing_stopped&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ActionCable&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;broadcast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;typing_stopped&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;uid&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;to_s&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;user_email&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; current_user.&lt;/span&gt;&lt;span&gt;email&lt;/span&gt;&lt;span&gt; } )&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unsubscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Any cleanup needed when channel is unsubscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This channel will stream from the &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; channel and then broadcast the &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;typing_stopped&lt;/code&gt; events to the channel. The &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; event will include the current user’s &lt;code dir=&quot;auto&quot;&gt;uid&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;user_email&lt;/code&gt;. This information is needed to do two things. One, the &lt;code dir=&quot;auto&quot;&gt;user_id&lt;/code&gt; will be used to filter out the currently typing user from seeing the typing indicator. Second, the email address is used to display the message so as not to send the data to the front end differently. The &lt;code dir=&quot;auto&quot;&gt;unsubscribed&lt;/code&gt; method was added to the channel by default during the generation of the channel but can be removed as it is not needed.&lt;/p&gt;
&lt;p&gt;With that out of the way, the next place to add some code is the markup by adjusting and adding some StimulusJS to the &lt;code dir=&quot;auto&quot;&gt;Posts&lt;/code&gt; form:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/posts/_form.html.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; form_with &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dom_id(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;my-4&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;any?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error_explanation&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pluralize(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.errors.count, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; prohibited this post from&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;being saved:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;full_messages&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; msg &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-group&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-group&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-controller&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; f.&lt;/span&gt;&lt;span&gt;text_area&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Enter your blab&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;form-control&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;rows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; f.&lt;/span&gt;&lt;span&gt;text_area&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;message&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Enter your blab&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;form-control&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;turbo_permanent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;keydown-&gt;typing#typing keyup-&gt;typing#typingStopped&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;rows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typingHint&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-text&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-typing-target&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions my-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; f.&lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btn btn-primary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We replace two lines, adding StimulusJS markup of &lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;div class=&quot;form-group&quot;&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;action: &apos;keydown-&gt;typing#typing keyup-&gt;typing#typingStopped&apos;&lt;/code&gt; for the StimulusJS controller to listen for keydown and keyup events. We add &lt;code dir=&quot;auto&quot;&gt;turbo_permanent: true&lt;/code&gt; so that the turbo morph updates will not clear out the form’s current state (i.e., text that has been typed or bound javascript that StimulusJS is handling). Finally, the &lt;code dir=&quot;auto&quot;&gt;data-typing-target=&quot;display&quot;&lt;/code&gt; will be used to update the message in the view when someone is typing.&lt;/p&gt;
&lt;p&gt;Finally, we can add the StimulusJS controller to the &lt;code dir=&quot;auto&quot;&gt;app/javascript/controllers&lt;/code&gt; directory:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/javascript/controllers/typing_controller.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Controller } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@hotwired/stimulus&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; consumer &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;channels/consumer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; debounce &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;lodash.debounce&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Controller&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; targets &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;display&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;typingStopped&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;debounce&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;typingStopped&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;subscription&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;consumer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;subscriptions&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;TypingChannel&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;received&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;uid&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;userId&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;displayTarget&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;innerHTML&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;user_email&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; is typing...&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_event&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;subscription&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;perform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typing&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;typingStopped&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_event&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;subscription&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;perform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;typing_stopped&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;userId&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;document&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;meta[name=current-user-id]&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;?.&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This controller has many moving parts; we’ll go through them by behavior.&lt;/p&gt;
&lt;p&gt;First, we create a getter for the &lt;code dir=&quot;auto&quot;&gt;userId&lt;/code&gt;, which will get the &lt;code dir=&quot;auto&quot;&gt;current-user-id&lt;/code&gt; from the meta tag in the head of the document, which was added way earlier when we first modified the &lt;code dir=&quot;auto&quot;&gt;application.html.erb&lt;/code&gt;. This will be used later in the event filtering process.&lt;/p&gt;
&lt;p&gt;Then, in the &lt;code dir=&quot;auto&quot;&gt;connect&lt;/code&gt; method, we create a subscription to the &lt;code dir=&quot;auto&quot;&gt;TypingChannel&lt;/code&gt; and then listen for the &lt;code dir=&quot;auto&quot;&gt;received&lt;/code&gt; event. The &lt;code dir=&quot;auto&quot;&gt;received&lt;/code&gt; happens when the broadcast is sent from the &lt;code dir=&quot;auto&quot;&gt;TypingChannel&lt;/code&gt;. We’ll come back to the &lt;code dir=&quot;auto&quot;&gt;if&lt;/code&gt; statement in a moment.&lt;/p&gt;
&lt;p&gt;In the view, we used &lt;code dir=&quot;auto&quot;&gt;keydown-&gt;typing#typing&lt;/code&gt; to call the &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; function when that event happens in the browser. The &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; function then calls the &lt;code dir=&quot;auto&quot;&gt;perform&lt;/code&gt; method on the subscription to send the &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; event to the &lt;code dir=&quot;auto&quot;&gt;TypingChannel&lt;/code&gt;. This event triggers the &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; method in the &lt;code dir=&quot;auto&quot;&gt;TypingChannel&lt;/code&gt; to broadcast the &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; event to the &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt; channel, which is handled by the &lt;code dir=&quot;auto&quot;&gt;received&lt;/code&gt; handler.&lt;/p&gt;
&lt;p&gt;Additionally, we used &lt;code dir=&quot;auto&quot;&gt;keyup-&gt;typing#typingStopped&lt;/code&gt; in the view to call the &lt;code dir=&quot;auto&quot;&gt;typingStopped&lt;/code&gt; function. Here, we debounce the &lt;code dir=&quot;auto&quot;&gt;typingStopped&lt;/code&gt; function to only call it once every 1000 milliseconds or one second. This function will prevent the &lt;code dir=&quot;auto&quot;&gt;typingStopped&lt;/code&gt; method from being called too frequently and basically allow the controller to wait for the typing to be stopped. Using a debounce function is a common pattern in JavaScript to prevent a function from being called too frequently. We are using &lt;code dir=&quot;auto&quot;&gt;debounce&lt;/code&gt; from the &lt;code dir=&quot;auto&quot;&gt;lodash&lt;/code&gt; library to do this and was added in the import statement at the top of the file. The trick to getting the StimulusJS-based keydown event to use the lodash debounce is to set the controller’s &lt;code dir=&quot;auto&quot;&gt;typingStopped&lt;/code&gt; function in the initializer to itself inside the debounce function call.&lt;/p&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;typingStopped&lt;/code&gt; function then calls the &lt;code dir=&quot;auto&quot;&gt;perform&lt;/code&gt; method on the subscription to broadcast the &lt;code dir=&quot;auto&quot;&gt;typing_stopped&lt;/code&gt; event to the &lt;code dir=&quot;auto&quot;&gt;TypingChannel&lt;/code&gt; much like the same round trip for &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To get the controller ready to display text, we added &lt;code dir=&quot;auto&quot;&gt;data-typing-target=&quot;display&quot;&lt;/code&gt; in the view to display the typing indicator and then added a &lt;code dir=&quot;auto&quot;&gt;static targets = [&quot;display&quot;];&lt;/code&gt; to allow this controller to interact with that element.&lt;/p&gt;
&lt;p&gt;Now, finally getting back to the code inside the &lt;code dir=&quot;auto&quot;&gt;received&lt;/code&gt; handler, we check if the &lt;code dir=&quot;auto&quot;&gt;uid&lt;/code&gt; in the data object does not equal the &lt;code dir=&quot;auto&quot;&gt;userId&lt;/code&gt; from the meta tag. If the &lt;code dir=&quot;auto&quot;&gt;uid&lt;/code&gt; is not equal to the &lt;code dir=&quot;auto&quot;&gt;userId&lt;/code&gt;, then we update the &lt;code dir=&quot;auto&quot;&gt;displayTarget&lt;/code&gt; with the user’s email and a message that they are typing. Inside the &lt;code dir=&quot;auto&quot;&gt;innerHTML&lt;/code&gt; setter, we use a ternary operator to check if the &lt;code dir=&quot;auto&quot;&gt;action&lt;/code&gt; in the data object equals &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt;. If it is, we set the &lt;code dir=&quot;auto&quot;&gt;innerHTML&lt;/code&gt; to the user’s email and a message they are typing. If the &lt;code dir=&quot;auto&quot;&gt;action&lt;/code&gt; is not equal to &lt;code dir=&quot;auto&quot;&gt;typing&lt;/code&gt;, then we set the &lt;code dir=&quot;auto&quot;&gt;innerHTML&lt;/code&gt; to an empty string which clears the message in the browser.&lt;/p&gt;
&lt;p&gt;Now, suppose you were to open two browsers (again, a current browser and an incognito would work!) and log into each user. In that case, you would see the typing indicator change in real time as one user types; the other would see the indicator on their browser!&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Anyway, closing out these tutorials always feel weird. Hope you learned something from this and leave a comment with any questions or feedback!&lt;/p&gt;
&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>Rails</category><category>Hotwire</category><category>Turbo 8</category><category>StimulusJS</category></item><item><title>Rails Console in the Browser (on-demand)</title><link>https://robrace.dev/blog/rails-console-in-the-browser/</link><guid isPermaLink="true">https://robrace.dev/blog/rails-console-in-the-browser/</guid><description>Using the Rails browser console in a safe way</description><pubDate>Tue, 24 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;div&gt;&lt;h2 id=&quot;why-in-the-browser&quot;&gt;Why in the browser?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;While doing some research and working on some details for my new book &lt;a href=&quot;https://realtimerails.dev&quot;&gt;Realtime Rails&lt;/a&gt;, I came across an issue where making changes to &lt;code dir=&quot;auto&quot;&gt;ActiveRecord&lt;/code&gt; objects in the terminal’s &lt;code dir=&quot;auto&quot;&gt;rails console&lt;/code&gt; were not showing up in the web browser. When working in development, based on default settings for ActionCable and the Rails server, for the broadcast to show up, it needs to be using the same process instead of the separate process spaned by the &lt;code dir=&quot;auto&quot;&gt;rails console&lt;/code&gt; in the terminal.&lt;/p&gt;
&lt;p&gt;Also, having the console show up in the browser on demand is a pretty cool setup. You can see the changes in real time and interact with the objects in the browser.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;how-to-set-it-up&quot;&gt;How to set it up&lt;/h2&gt;&lt;/div&gt;
&lt;aside aria-label=&quot;Tip&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Tip&lt;/p&gt;&lt;div&gt;&lt;p&gt;If you have a recent version of Rails, this gem should already be included, as Rails has been adding the &lt;code dir=&quot;auto&quot;&gt;web-console&lt;/code&gt; gem to the default Gemfile for a while now to show the console in the browser on exception pages.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you need to set up the Rails console in the browser yourself instead, you will need to add the &lt;code dir=&quot;auto&quot;&gt;web-console&lt;/code&gt; gem to your &lt;code dir=&quot;auto&quot;&gt;Gemfile&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gem &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;web-console&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;group&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;development&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then, run &lt;code dir=&quot;auto&quot;&gt;bundle install&lt;/code&gt; to install the gem.&lt;/p&gt;
&lt;p&gt;Once installed, you can add a web console to any view file by adding &lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;%= console %&gt;&lt;/code&gt;, then interact with the Rails console in the browser.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;taking-it-a-step-further&quot;&gt;Taking it a step further&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you want to add some convenience to this process, you can add route, controller, and view (that are development only) to have the browser show up in the browser on a specific page in your application.&lt;/p&gt;
&lt;p&gt;First, adding the development-only route:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;development?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;/console&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;console#show&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then, create the controller:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ConsoleController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;show&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;And finally, the view:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Rails Console&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; console &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, when you visit &lt;code dir=&quot;auto&quot;&gt;/console&lt;/code&gt; in your development environment, you will have a console in the browser.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;alternate-idea&quot;&gt;Alternate Idea&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Another idea is to add a link to the console in your application’s footer. This way, you can easily access the console from any page in your application.&lt;/p&gt;
&lt;aside aria-label=&quot;Note&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Note&lt;/p&gt;&lt;div&gt;&lt;p&gt;Web Console has CSS to make it sticky to the bottom of the page, so it will always be visible. There is no extra CSS to add when you want to add to any existing pages in your application.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Then, add the link to the console in your &lt;code dir=&quot;auto&quot;&gt;application.html.erb&lt;/code&gt; layout file, but since we’re not relying on the route to show the console in the development environment only, we will need to add a conditional check in the template:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;development?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; console &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Personally, I’d rather have the console appear on a specific page, but this is a good alternative if you want the console available on every page in your application.&lt;/p&gt;
&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>Rails</category></item><item><title>How to Build a Twitter Clone with Rails 8 and Turbo 8</title><link>https://robrace.dev/blog/build-a-twitter-clone-with-rails-8-and-turbo-8/</link><guid isPermaLink="true">https://robrace.dev/blog/build-a-twitter-clone-with-rails-8-and-turbo-8/</guid><description>An update to How to Build a Twitter Clone with Rails and Hotwire</description><pubDate>Mon, 16 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hotwire, StimulusJS, and Turbo have been out for the better part of half a decade, and there have been sweeping changes in the Turbo Broadcast and Stream patterns this past year. All of this gets bundled with Rails 8, plus other new features to make development even quicker, such as SolidQueue, SolidCache, and SolidCable. Two of those three make it dead simple to get this clone up and running super quick!&lt;/p&gt;
&lt;aside aria-label=&quot;Note&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Note&lt;/p&gt;&lt;div&gt;&lt;p&gt;I use Bootstrap when making something quick. You can throw an asset tag in the &lt;code dir=&quot;auto&quot;&gt;views/layout/application.html.erb&lt;/code&gt; or add Bootstrap to the &lt;code dir=&quot;auto&quot;&gt;rails --new&lt;/code&gt; command (but have yarn installed before running &lt;code dir=&quot;auto&quot;&gt;rails new&lt;/code&gt;). Additionally, I am using the &lt;code dir=&quot;auto&quot;&gt;--main&lt;/code&gt; flag to install Rails from the main branch in Github to get the currently unreleased but nearly ready Rails 8.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Without further ado, let’s get started with a new app:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blabber&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--main&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--css=bootstrap&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Rails will do its thing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install the application.&lt;/li&gt;
&lt;li&gt;Install the gems.&lt;/li&gt;
&lt;li&gt;Process the Bootstrap installation.&lt;/li&gt;
&lt;li&gt;Install the yarn packages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hotwire and Turbo are now part of Rails, so installing anything extra is unnecessary! Just fire up &lt;code dir=&quot;auto&quot;&gt;bin/dev&lt;/code&gt; and start writing some code.&lt;/p&gt;
&lt;p&gt;Now, let’s make a model to hold the data to clone a tweet in this Twitter clone called Blabber. All basic attributes:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;body:text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;likes_count:integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repost_count:integer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To keep this closely resembling the &lt;a href=&quot;https://robrace.dev/build-a-twitter-clone-with-rails-hotwire&quot;&gt;previous article&lt;/a&gt;, we’ll add the same validation in the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; model:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/models/post.rb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;broadcasts_refreshes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;minimum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;280&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;One difference you see is the &lt;code dir=&quot;auto&quot;&gt;broadcasts_refreshes&lt;/code&gt; line. This line is part of Turbo 8, where the backend changes (here, changes to the model) are broadcast as a &lt;code dir=&quot;auto&quot;&gt;refresh&lt;/code&gt; event in Turbo, to be picked up by the &lt;code dir=&quot;auto&quot;&gt;turbo.js&lt;/code&gt; front end. Hidden behind the scenes is all the work Rails does to connect browsers, expecting that particular model instance to have updates and all the connecting parts such as ActiveJob, ActionCable, SolidQueue, and SolidCable.&lt;/p&gt;
&lt;p&gt;We’ll make the same few minor adjustments to the generated migration file to add some database-level defaults (and allow us to keep the code around &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; creation simple):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreatePosts&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActiveRecord::Migration&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;6.0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;create_table &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Blabby&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;timestamps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Ok! Now, we’re ready to run that migration!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Another change with this process of &lt;code dir=&quot;auto&quot;&gt;refreshes&lt;/code&gt; and streams is that our controller can now be unaware of the streams and does not need to have format-based responses or template files that closely resemble the same partials used for the non-dynamic part of the app.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PostsController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(post_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;respond_to &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post_params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;params.&lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;permit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Simple controller. The &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; action returns a list of posts to &lt;code dir=&quot;auto&quot;&gt;@post&lt;/code&gt;. &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; uses &lt;code dir=&quot;auto&quot;&gt;StrongParameters&lt;/code&gt;, creates a new Post, and redirects back to the &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; template. &lt;code dir=&quot;auto&quot;&gt;like&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;repost&lt;/code&gt; are similar, except they increment the respective count columns.&lt;/p&gt;
&lt;p&gt;Let’s wire up a few routes to match up to those controller actions. Yes, these aren’t perfect RESTful routes, but 1) They work. 2) This is a 10-minute tutorial. 3) POST requests did not get the other browser window to update with the changes in my small app here. (If anyone knows why, I’d be happy to hear!)&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; %i[&lt;/span&gt;&lt;span&gt;index create&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts#index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With a Model, Controller, and routes, we can put together some view templates. We can now add the “magic sauce” to the application layout to listen for &lt;code dir=&quot;auto&quot;&gt;refresh&lt;/code&gt; broadcasts.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= content_for(:title) || &quot;Blabber&quot; %&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;viewport&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;width=device-width,initial-scale=1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;apple-mobile-web-app-capable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;yes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mobile-web-app-capable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;yes&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= csrf_meta_tags %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= csp_meta_tag %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= turbo_refreshes_with method: :morph, scroll: :preserve %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= yield :head %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%#= tag.link rel: &quot;manifest&quot;, href: pwa_manifest_path %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;image/png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.svg&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;image/svg+xml&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;apple-touch-icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/icon.png&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%# Includes all stylesheet files in app/views/stylesheets %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= stylesheet_link_tag :app, &quot;data-turbo-track&quot;: &quot;reload&quot; %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= javascript_importmap_tags %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= stylesheet_link_tag &quot;application&quot;, &quot;data-turbo-track&quot;: &quot;reload&quot; %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= yield %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;yield :head&lt;/code&gt; by the &lt;code dir=&quot;auto&quot;&gt;rails new&lt;/code&gt; command, and just above it, we added &lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;%= turbo_refreshes_with method: :morph, scroll: :preserve  %&gt;&lt;/code&gt; to tell the application to listen for &lt;code dir=&quot;auto&quot;&gt;refreshes.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The first view template up is the &lt;code dir=&quot;auto&quot;&gt;app/views/posts/index.html.erb&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A Rails, Hotwire demo&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= render partial: &apos;form&apos; %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= turbo_stream_from &apos;posts&apos; %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= render @posts %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The view is a relatively simple Rails template. It uses a &lt;code dir=&quot;auto&quot;&gt;render @posts&lt;/code&gt; method to render the collection of &lt;code dir=&quot;auto&quot;&gt;@posts&lt;/code&gt; using a &lt;code dir=&quot;auto&quot;&gt;posts/_post&lt;/code&gt; partial. The &lt;code dir=&quot;auto&quot;&gt;turbo_stream_from:posts&lt;/code&gt; hooks up the view layer to listen to the Post model’s broadcasts.&lt;/p&gt;
&lt;p&gt;The partial is some pretty standard singular object markup with some Bootstrap classes thrown in to make it look half-decent:&lt;/p&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;app/views/posts/_posts.html.erb&lt;/code&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= turbo_stream_from post %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= dom_id(post) %&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-title text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; Posted at &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.created_at %&gt; &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.username %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-text lead mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.body %&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= link_to post_repost_path(post), { class: &apos;card-link&apos;, data: { turbo_prefetch: false }} do %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Repost (&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.repost_count %&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= link_to post_like_path(post), { class: &apos;card-link&apos;, data: { turbo_prefetch: false }} do %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Likes (&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.likes_count %&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Another &lt;code dir=&quot;auto&quot;&gt;turbo_stream&lt;/code&gt; command is used to listen for broadcasted updates and deletions, and the &lt;code dir=&quot;auto&quot;&gt;dom_id&lt;/code&gt; helper is used to create conventional Rails View IDs &lt;code dir=&quot;auto&quot;&gt;model_resource-id&lt;/code&gt;. In this case, it would look like &lt;code dir=&quot;auto&quot;&gt;posts_1&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;Up next is a straightforward Rails form:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= form_with model: @post, id: dom_id(@post), html: {class: &apos;my-4&apos; } do |f| %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% if @post.errors.any? %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error_explanation&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= pluralize(@post.errors.count, &quot;error&quot;) %&gt; prohibited this post from&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;being saved:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% @post.errors.full_messages.each do |msg| %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= msg %&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-group&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= f.text_area :body, placeholder: &apos;Enter your blab&apos;, class: &apos;form-control&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rows: 3 %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= f.submit class: &quot;btn btn-primary&quot; %&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you remember the &lt;code dir=&quot;auto&quot;&gt;index.html.erb&lt;/code&gt;, another frame wrapped the form’s render call. Thus, we included the &lt;code dir=&quot;auto&quot;&gt;dom_id&lt;/code&gt; to allow the controller’s &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; action to enable Turbo to manage the streams’ markup.&lt;/p&gt;
&lt;p&gt;Now we’ll head back to the controller to talk about the create action in depth:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(post_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;respond_to &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Previously, at this point, we’d discuss returning streams in the markup to be appended to the top-level frame, but Turbo 8’s refreshes and broadcasts manage all of that without any extra changes.&lt;/p&gt;
&lt;p&gt;At this point, you should have an app that you can open in two browsers, which should look like this!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://robrace.dev/.netlify/images?url=_astro%2Fblabber.Dy0y0QuK.gif&amp;#x26;w=577&amp;#x26;h=196&amp;#x26;dpl=6ab305c7c94948000856228f&quot; alt=&quot;Blabber Gif&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;577&quot; height=&quot;196&quot;&gt;&lt;/p&gt;
&lt;p&gt;There you go! If everything went well, this was the fastest Twitter clone tutorial yet and probably required the least lines of code! Time will tell how all of this will play well together and scale in complex real-world Rails applications, but for now, the reduction in developer complexity is a huge win!&lt;/p&gt;
&lt;div id=&quot;disqus_thread&quot;&gt;&lt;/div&gt;</content:encoded><category>Turbo 8</category><category>Rails</category></item><item><title>How to get a User&apos;s Timezone into Rails</title><link>https://robrace.dev/blog/how-to-get-a-users-timezone-into-rails/</link><guid isPermaLink="true">https://robrace.dev/blog/how-to-get-a-users-timezone-into-rails/</guid><description>Quick tutorial to get time zones across the stack</description><pubDate>Thu, 27 Jul 2023 15:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You may have noticed most applications that you interact with &lt;em&gt;and&lt;/em&gt; care about your timezone will, at one point, as you for your timezone &lt;em&gt;and&lt;/em&gt; normally prepopulate the dropdown with your location’s timezone.&lt;/p&gt;
&lt;p&gt;However, if you look for this functionality out-of-the-box in Rails, it does not seem to be there (or at least through all my sleuthing for &lt;a href=&quot;https://statuscat.com&quot;&gt;My new app I am building&lt;/a&gt;). Though, I have the answer for how you can add this in about ten lines of code through one StimulusJS controller and your neighborhood Rails helpers.&lt;/p&gt;
&lt;p&gt;First thing first, you will want to ditch the &lt;code dir=&quot;auto&quot;&gt;time_zone_select&lt;/code&gt; helper as the values used in there are not the same as what the browser will use in identifying time zones (I think Rails is not using the standard here, but I didn’t dig into too much while solving the technical issue). Instead, we will use a regular select and modify the array sent to a select tag:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data-controller&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;timezone&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; f.&lt;/span&gt;&lt;span&gt;select&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;timezone&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ActiveSupport&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;TimeZone&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;collect&lt;/span&gt;&lt;span&gt; {|&lt;/span&gt;&lt;span&gt;tz&lt;/span&gt;&lt;span&gt;| [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;(UTC&lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;ActiveSupport&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;TimeZone&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;seconds_to_utc_offset&lt;/span&gt;&lt;span&gt;(tz.&lt;/span&gt;&lt;span&gt;utc_offset&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;tz.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, tz.&lt;/span&gt;&lt;span&gt;tzinfo&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;]}, {}, &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;timezone-target&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tz&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When using a select tag, you can provide the second argument an array of strings to represent the “text” and option “value” in the dropdown options. Here &lt;code dir=&quot;auto&quot;&gt;&quot;(UTC#{ActiveSupport::TimeZone.seconds_to_utc_offset(tz.utc_offset)}) #{tz.name}&quot;&lt;/code&gt; will construct a string with the offset and Rails timezone name (i.e., &lt;code dir=&quot;auto&quot;&gt;(UTC-08:00) Pacific Time (US &amp;#x26; Canada)&lt;/code&gt;) and have the options value as the more accepted &lt;code dir=&quot;auto&quot;&gt;America/Los_Angeles&lt;/code&gt;. Additionally, at the very end, you can see where we are setting up the StimulusJS controller to interact with our select element, &lt;code dir=&quot;auto&quot;&gt;data: {&quot;timezone-target&quot; =&gt; &quot;tz&quot;}&lt;/code&gt;, which will produce &lt;code dir=&quot;auto&quot;&gt;data-timezone-target=&quot;tz&quot;&lt;/code&gt; in the markup. Lastly, this select tag was wrapped in a &lt;code dir=&quot;auto&quot;&gt;div&lt;/code&gt; with an attribute to use the &lt;code dir=&quot;auto&quot;&gt;Timezone&lt;/code&gt; controller, &lt;code dir=&quot;auto&quot;&gt;data-controller=&quot;timezone&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we can jump to the StimulusJS controller, which will also be simple. It is easiest to use the Rails CLI command to create the controller and update any other files that include controllers into the &lt;code dir=&quot;auto&quot;&gt;application.js&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stimulus&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;timezone&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - If you do not have Stimulus installed, you can do so with instructions here: &lt;a href=&quot;https://github.com/hotwired/stimulus-rails&quot;&gt;https://github.com/hotwired/stimulus-rails&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the new &lt;code dir=&quot;auto&quot;&gt;timezone_controller.js&lt;/code&gt;, you can add the following code:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Controller } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@hotwired/stimulus&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Controller&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;static&lt;/span&gt;&lt;span&gt; targets &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;tz&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;tzTarget&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Intl&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DateTimeFormat&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;resolvedOptions&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;timeZone&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;Intl.DateTimeFormat().resolvedOptions().timeZone;&lt;/code&gt; is the browser’s javascript command for retrieving the timezone. Then, &lt;code dir=&quot;auto&quot;&gt;this.tzTarget.value = &lt;/code&gt; sets the dropdown value when the StimulusJS controller initializes.&lt;/p&gt;
&lt;p&gt;That’s it! About five lines of custom code and another handful of edits to existing Rails tools to pre-select the user’s timezone in your Rails application!&lt;/p&gt;
&lt;p&gt;This code, as-is, will not work for select boxes that need to be updated as it does not account for not overwriting an existing selected timezone provided by the backend. That’d be as easy as checking for the selected target’s value in Stimulus.&lt;/p&gt;
</content:encoded><category>Rails</category></item><item><title>How to Build a Twitter Clone with Rails and Hotwire</title><link>https://robrace.dev/blog/build-a-twitter-clone-with-rails-hotwire/</link><guid isPermaLink="true">https://robrace.dev/blog/build-a-twitter-clone-with-rails-hotwire/</guid><description>An update to How to Build a Twitter Clone with rails, ActionCable, and React</description><pubDate>Sun, 27 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hotwire is fresh out of the Basecamp Github and has set the Rails community ablaze. This quick write up is an update from &lt;a href=&quot;https://robrace.dev/build-a-twitter-clone-with-rails-actioncable-and-react/&quot;&gt;How to Build a Twitter Clone with rails, ActionCable, and React&lt;/a&gt; to show a comparison between the two approaches.&lt;/p&gt;
&lt;p&gt;Now, this doesn’t mean to throw away any React code you may be using or StimulusReflex if you have already gone down that path. Those approaches are both more mature and widely discoverable.&lt;/p&gt;
&lt;p&gt;I may follow this up with a video to do a more side-by-side look at these two approaches.&lt;/p&gt;
&lt;p&gt;Without further ado, let’s get started with a new app:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails new blabber --no-spring&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Rails will do its thing, install the application, install the gems, process the Webpacker install, and install the NPM packages.&lt;/p&gt;
&lt;p&gt;Next, we will want to add the &lt;code dir=&quot;auto&quot;&gt;hotwire-rails&lt;/code&gt; gem to get the libraries needed:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gem &quot;hotwire-rails&quot;, &quot;~&gt; 0.1.1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Bundle and run the installer command for Hotwire:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bundle install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails hotwire:install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, let’s make a model to hold the data to clone a tweet in this Twitter clone called Blabber. All basic attributes:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails g model Post username body:text likes_count:integer repost_count:integer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To keep this closely resembling the CableReady/StimulusReflex/React article, we’ll add the same validation in the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; model:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;after_create_commit { broadcast_prepend_later_to &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;after_update_commit { broadcast_replace_later_to &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;after_destroy_commit { broadcast_remove_to &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;minimum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;280&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;One difference you see is the callbacks. When using Hotwire, the prevailing convention is to use &lt;code dir=&quot;auto&quot;&gt;ActiveRecord&lt;/code&gt; callbacks. While working on the model collection, you can set up callbacks for create, update, and destroy broadcasts.&lt;/p&gt;
&lt;p&gt;We’ll make a few small adjustments to the generated migration file to add some database-level defaults (and allows us to keep the code around &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; creation simple):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreatePosts&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActiveRecord::Migration&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;6.0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;create_table &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Blabby&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;timestamps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Ok! Now, we’re ready to run that migration!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the Model and Database layer out of the way, we can move on to the controller and corresponding view templates!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(post_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;respond_to &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; { redirect_to posts_path }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; { render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;turbo_stream&lt;/span&gt;&lt;span&gt; { render &lt;/span&gt;&lt;span&gt;turbo_stream&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; turbo_stream.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts/form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;locals&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; }) } &lt;/span&gt;&lt;span&gt;## New for this article&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;## New for this article&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;## New for this article&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post_params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;params.&lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;permit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Simple controller. The &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; action returns a list of posts to &lt;code dir=&quot;auto&quot;&gt;@post&lt;/code&gt;. &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; uses &lt;code dir=&quot;auto&quot;&gt;StrongParameters&lt;/code&gt;, creates a new Post, and addresses some  &lt;code dir=&quot;auto&quot;&gt;Turbo::Stream&lt;/code&gt; usage (more on that soon), and redirects back to the &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; template. &lt;code dir=&quot;auto&quot;&gt;like&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;repost&lt;/code&gt; are similar except they increment the respective count columns.&lt;/p&gt;
&lt;p&gt;Let’s wire up a few routes to match up to those controller actions. Yes, these aren’t perfect RESTful routes, but 1) They work. 2) This is a 10-minute tutorial. 3) Are GET requests making sure we do not have to worry about AJAX/fetch/CSRF in the front-end. You would work around these issues in a production application.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; %i[&lt;/span&gt;&lt;span&gt;index create&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts#index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With a Model, Controller, and routes, we can put together some view templates. We’ll start by adding &lt;code dir=&quot;auto&quot;&gt;Bootstrap&lt;/code&gt; CDN CSS. This way, we can wire up some UI interfaces pretty quickly!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= csrf_meta_tags %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= csp_meta_tag %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= stylesheet_link_tag &apos;application&apos;, media: &apos;all&apos;, &apos;data-turbolinks-track&apos;: &apos;reload&apos; %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stylesheet&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;integrity&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crossorigin&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;anonymous&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= yield :head %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= turbo_include_tags %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= stimulus_include_tags %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= yield %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;yield :head&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;turbo_include_tags&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;stimulus_include_tags&lt;/code&gt; were all added earlier by the &lt;code dir=&quot;auto&quot;&gt;hotwire:install&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;The first view template up is the &lt;code dir=&quot;auto&quot;&gt;app/views/posts/index.html.erb&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A Rails, Hotwire demo&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= turbo_frame_tag &apos;post_form&apos; do %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= render partial: &apos;form&apos; %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= turbo_stream_from :posts %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= turbo_frame_tag &apos;posts&apos; do %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= render @posts %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The view is a relatively simple usage of Rails, with a &lt;code dir=&quot;auto&quot;&gt;render @posts&lt;/code&gt; method, rendering the collection of &lt;code dir=&quot;auto&quot;&gt;@posts&lt;/code&gt;, using a &lt;code dir=&quot;auto&quot;&gt;posts/_post&lt;/code&gt; partial. The &lt;code dir=&quot;auto&quot;&gt;turbo_frame_tag&lt;/code&gt;’s will allow the &lt;code dir=&quot;auto&quot;&gt;Turbo&lt;/code&gt; libraries to instantiate the “frames” that will manage the HTML partials sent over the wire from the broadcasted changes. A lot is going on with that process, but Hotwire and Rails are taking care of it all and merely managing the markup. Specifically, &lt;code dir=&quot;auto&quot;&gt;turbo_stream_from :posts&lt;/code&gt; hooks up the view layer to listen to the Post model’s broadcasts.&lt;/p&gt;
&lt;p&gt;The partial is some pretty standard singular object markup with some Bootstrap classes thrown in to make it look half decent:&lt;/p&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;app/views/posts/_posts.html.erb&lt;/code&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= turbo_stream_from post %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= dom_id(post) %&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-title text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Posted at &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.created_at %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.username %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-text lead mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.body %&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post_repost_path(post) %&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Repost (&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.repost_count %&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post_like_path(post) %&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Likes (&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= post.likes_count %&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Another &lt;code dir=&quot;auto&quot;&gt;turbo_stream&lt;/code&gt; command to listen for broadcasted updates and deletions, and the &lt;code dir=&quot;auto&quot;&gt;dom_id&lt;/code&gt; helper to create conventional Rails View IDs &lt;code dir=&quot;auto&quot;&gt;model_resource-id&lt;/code&gt;. In this case, it would look like &lt;code dir=&quot;auto&quot;&gt;posts_1&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;Up next is a straightforward Rails form:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= form_with model: @post, id: dom_id(@post), html: {class: &apos;my-4&apos; } do |f| %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% if @post.errors.any? %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error_explanation&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= pluralize(@post.errors.count, &quot;error&quot;) %&gt; prohibited this post from&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;being saved:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% @post.errors.full_messages.each do |msg| %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= msg %&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-group&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= f.text_area :body, placeholder: &apos;Enter your blab&apos;, class: &apos;form-control&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rows: 3 %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= f.submit class: &quot;btn btn-primary&quot; %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you remember the &lt;code dir=&quot;auto&quot;&gt;index.html.erb&lt;/code&gt;, another frame wrapped the form’s render call. Thus we included the &lt;code dir=&quot;auto&quot;&gt;dom_id&lt;/code&gt; to allow the controller’s &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; action to enable Turbo to manage the streams’ markup.&lt;/p&gt;
&lt;p&gt;Now we’ll head back to the controller to talk about the create action in depth:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(post_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;respond_to &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; { redirect_to posts_path }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; { render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;turbo_stream&lt;/span&gt;&lt;span&gt; { render &lt;/span&gt;&lt;span&gt;turbo_stream&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; turbo_stream.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts/form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;locals&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; }) }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;On a failed save, it will instruct the form’s partial to update the form frame with the form template, but this time with the errors markup triggered.&lt;/p&gt;
&lt;p&gt;At this point, it should look like this!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://robrace.dev/.netlify/images?url=_astro%2Fblabber_low.BUHPb2dl.gif&amp;#x26;w=640&amp;#x26;h=235&amp;#x26;dpl=6ab305c7c94948000856228f&quot; alt=&quot;Blabber Gif&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;235&quot;&gt;&lt;/p&gt;
&lt;p&gt;There you go! I bet with a fast enough system to work through the Rails install, gem install, and javascript package installs, you could make it through this tutorial in less than 10 minutes!&lt;/p&gt;</content:encoded><category>Hotwire</category><category>Rails</category></item><item><title>How to Build a Twitter Clone with Rails and Hotwire</title><link>https://robrace.dev/blog/rails-hotwire/</link><guid isPermaLink="true">https://robrace.dev/blog/rails-hotwire/</guid><description>An update to How to Build a Twitter Clone with rails, ActionCable, and React</description><pubDate>Sun, 27 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hotwire is fresh out of the Basecamp Github and has set the Rails community ablaze. This quick write up is an update from &lt;a href=&quot;https://robrace.dev/build-a-twitter-clone-with-rails-actioncable-and-react/&quot;&gt;How to Build a Twitter Clone with rails, ActionCable, and React&lt;/a&gt; to show a comparison between the two approaches.&lt;/p&gt;
&lt;p&gt;Now, this doesn’t mean to throw away any React code you may be using or StimulusReflex if you have already gone down that path. Those approaches are both more mature and widely discoverable.&lt;/p&gt;
&lt;p&gt;I may follow this up with a video to do a more side-by-side look at these two approaches.&lt;/p&gt;
&lt;p&gt;Without further ado, let’s get started with a new app:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blabber&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--no-spring&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Rails will do its thing, install the application, install the gems, process the Webpacker install, and install the NPM packages.&lt;/p&gt;
&lt;p&gt;Next, we will want to add the &lt;code dir=&quot;auto&quot;&gt;hotwire-rails&lt;/code&gt; gem to get the libraries needed:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gem &quot;hotwire-rails&quot;, &quot;~&gt; 0.1.1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Bundle and run the installer command for Hotwire:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;hotwire:install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, let’s make a model to hold the data to clone a tweet in this Twitter clone called Blabber. All basic attributes:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;body:text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;likes_count:integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repost_count:integer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To keep this closely resembling the CableReady/StimulusReflex/React article, we’ll add the same validation in the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; model:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;after_create_commit { broadcast_prepend_later_to &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;after_update_commit { broadcast_replace_later_to &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;after_destroy_commit { broadcast_remove_to &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;minimum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;280&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;One difference you see is the callbacks. When using Hotwire, the prevailing convention is to use &lt;code dir=&quot;auto&quot;&gt;ActiveRecord&lt;/code&gt; callbacks. While working on the model collection, you can set up callbacks for create, update, and destroy broadcasts.&lt;/p&gt;
&lt;p&gt;We’ll make a few small adjustments to the generated migration file to add some database-level defaults (and allows us to keep the code around &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; creation simple):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreatePosts&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActiveRecord::Migration&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;6.0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;create_table &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Blabby&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;timestamps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Ok! Now, we’re ready to run that migration!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the Model and Database layer out of the way, we can move on to the controller and corresponding view templates!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(post_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;respond_to &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; { redirect_to posts_path }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; { render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;turbo_stream&lt;/span&gt;&lt;span&gt; { render &lt;/span&gt;&lt;span&gt;turbo_stream&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; turbo_stream.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts/form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;locals&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; }) } &lt;/span&gt;&lt;span&gt;## New for this article&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;## New for this article&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;## New for this article&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post_params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;params.&lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;permit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Simple controller. The &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; action returns a list of posts to &lt;code dir=&quot;auto&quot;&gt;@post&lt;/code&gt;. &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; uses &lt;code dir=&quot;auto&quot;&gt;StrongParameters&lt;/code&gt;, creates a new Post, and addresses some  &lt;code dir=&quot;auto&quot;&gt;Turbo::Stream&lt;/code&gt; usage (more on that soon), and redirects back to the &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; template. &lt;code dir=&quot;auto&quot;&gt;like&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;repost&lt;/code&gt; are similar except they increment the respective count columns.&lt;/p&gt;
&lt;p&gt;Let’s wire up a few routes to match up to those controller actions. Yes, these aren’t perfect RESTful routes, but 1) They work. 2) This is a 10-minute tutorial. 3) Are GET requests making sure we do not have to worry about AJAX/fetch/CSRF in the front-end. You would work around these issues in a production application.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; %i[&lt;/span&gt;&lt;span&gt;index create&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts#index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With a Model, Controller, and routes, we can put together some view templates. We’ll start by adding &lt;code dir=&quot;auto&quot;&gt;Bootstrap&lt;/code&gt; CDN CSS. This way, we can wire up some UI interfaces pretty quickly!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csrf_meta_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csp_meta_tag &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; stylesheet_link_tag &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;data-turbolinks-track&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stylesheet&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;integrity&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crossorigin&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;anonymous&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_include_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; stimulus_include_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code dir=&quot;auto&quot;&gt;yield :head&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;turbo_include_tags&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;stimulus_include_tags&lt;/code&gt; were all added earlier by the &lt;code dir=&quot;auto&quot;&gt;hotwire:install&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;The first view template up is the &lt;code dir=&quot;auto&quot;&gt;app/views/posts/index.html.erb&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A Rails, Hotwire demo&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_frame_tag &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;post_form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_stream_from &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_frame_tag &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The view is a relatively simple usage of Rails, with a &lt;code dir=&quot;auto&quot;&gt;render @posts&lt;/code&gt; method, rendering the collection of &lt;code dir=&quot;auto&quot;&gt;@posts&lt;/code&gt;, using a &lt;code dir=&quot;auto&quot;&gt;posts/_post&lt;/code&gt; partial. The &lt;code dir=&quot;auto&quot;&gt;turbo_frame_tag&lt;/code&gt;’s will allow the &lt;code dir=&quot;auto&quot;&gt;Turbo&lt;/code&gt; libraries to instantiate the “frames” that will manage the HTML partials sent over the wire from the broadcasted changes. A lot is going on with that process, but Hotwire and Rails are taking care of it all and merely managing the markup. Specifically, &lt;code dir=&quot;auto&quot;&gt;turbo_stream_from :posts&lt;/code&gt; hooks up the view layer to listen to the Post model’s broadcasts.&lt;/p&gt;
&lt;p&gt;The partial is some pretty standard singular object markup with some Bootstrap classes thrown in to make it look half decent:&lt;/p&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;app/views/posts/_posts.html.erb&lt;/code&gt;&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; turbo_stream_from post &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dom_id(post)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-title text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Posted at &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-text lead mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post_repost_path(post)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Repost (&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post_like_path(post)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Likes (&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; post.&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Another &lt;code dir=&quot;auto&quot;&gt;turbo_stream&lt;/code&gt; command to listen for broadcasted updates and deletions, and the &lt;code dir=&quot;auto&quot;&gt;dom_id&lt;/code&gt; helper to create conventional Rails View IDs &lt;code dir=&quot;auto&quot;&gt;model_resource-id&lt;/code&gt;. In this case, it would look like &lt;code dir=&quot;auto&quot;&gt;posts_1&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;Up next is a straightforward Rails form:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; form_with &lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dom_id(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;my-4&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;any?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error_explanation&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pluralize(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.errors.count, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; prohibited this post from&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;being saved:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h2&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;full_messages&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;msg&lt;/span&gt;&lt;span&gt;| &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; msg &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-group&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; f.&lt;/span&gt;&lt;span&gt;text_area&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Enter your blab&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;form-control&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;rows&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; f.&lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btn btn-primary&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;%&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;If you remember the &lt;code dir=&quot;auto&quot;&gt;index.html.erb&lt;/code&gt;, another frame wrapped the form’s render call. Thus we included the &lt;code dir=&quot;auto&quot;&gt;dom_id&lt;/code&gt; to allow the controller’s &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; action to enable Turbo to manage the streams’ markup.&lt;/p&gt;
&lt;p&gt;Now we’ll head back to the controller to talk about the create action in depth:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(post_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;respond_to &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; { redirect_to posts_path }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; { render &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;format&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;turbo_stream&lt;/span&gt;&lt;span&gt; { render &lt;/span&gt;&lt;span&gt;turbo_stream&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; turbo_stream.&lt;/span&gt;&lt;span&gt;replace&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts/form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;locals&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; }) }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;On a failed save, it will instruct the form’s partial to update the form frame with the form template, but this time with the errors markup triggered.&lt;/p&gt;
&lt;p&gt;At this point, it should look like this!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Blabber Gif&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;640&quot; height=&quot;235&quot; src=&quot;https://robrace.dev/.netlify/images?url=_astro%2Fblabber_low.BUHPb2dl.gif&amp;#x26;w=640&amp;#x26;h=235&amp;#x26;dpl=6ab305c7c94948000856228f&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;There you go! I bet with a fast enough system to work through the Rails install, gem install, and javascript package installs, you could make it through this tutorial in less than 10 minutes!&lt;/p&gt;
</content:encoded><category>Hotwire</category><category>Rails</category></item><item><title>Using Action Mailbox in Rails 6 to Receive Mail</title><link>https://robrace.dev/blog/using-action-mailbox-in-rails-6-to-receive-mail/</link><guid isPermaLink="true">https://robrace.dev/blog/using-action-mailbox-in-rails-6-to-receive-mail/</guid><pubDate>Sun, 12 Jul 2020 19:00:08 GMT</pubDate><content:encoded>&lt;p&gt;Sending mail from a Rails application has been covered by hundreds or thousands of articles, however, there is not a ton of articles about receiving, parsing and using the new &lt;code dir=&quot;auto&quot;&gt;ActionMailbox&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The following is an excerpt from my book &lt;a href=&quot;https://buildasaasappinrails.com&quot;&gt;Build A SaaS App in Rails 6&lt;/a&gt; and is used in a new product I am building &lt;a href=&quot;https://mailbuffer.io&quot;&gt;Mail Buffer&lt;/a&gt;. Thus, I am standing by the tutorial here with real world usage!&lt;/p&gt;
&lt;p&gt;Lastly, as this is an excerpt, this takes into account the chapter leading up to this section where the reader will have created an application, models for a &lt;code dir=&quot;auto&quot;&gt;Standup&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;Todo&lt;/code&gt;/&lt;code dir=&quot;auto&quot;&gt;Did&lt;/code&gt;/&lt;code dir=&quot;auto&quot;&gt;Blockers&lt;/code&gt; (which are &lt;code dir=&quot;auto&quot;&gt;Tasks&lt;/code&gt;) and a few mailers for outgoing mail.&lt;/p&gt;
&lt;p&gt;Here goes nothing…&lt;/p&gt;
&lt;p&gt;Another aspect of email within a SaaS application is receiving mail. While this is far less normal or used in comparison to sending, it can be a great way to make end user’s responses to email or action items quicker.&lt;/p&gt;
&lt;p&gt;At a high level, there are a few different layers to this. The topmost layer is the email service, and this book’s case, Sendgrid. This service handles sending outbound emails, as well as routing incoming emails to an address/domain name specified in their interface. Once the email is routed, it will be redirected to a route and processor file in your application. This file will be responsible for parsing the incoming email address and using logic to decide what to do with it.&lt;/p&gt;
&lt;p&gt;In the case of the Standup App we are building, we can have directions to respond to an Email Reminder to create a new standup right from their email response! Some of the tools that we will be using are Sendgrid’s email routing service and ngrok, an HTTP tunneling service. &lt;code dir=&quot;auto&quot;&gt;ngrok&lt;/code&gt; is very useful for a few solutions throughout the remainder of this book. It allows you to have a web-accessible URL, which tunnels(connects) to ports within your local machine. Meaning, that you will have a &lt;code dir=&quot;auto&quot;&gt;http://somesubdomain.ngrok.com&lt;/code&gt; that will forward to your computer and a specific port specified when you start &lt;code dir=&quot;auto&quot;&gt;ngrok&lt;/code&gt;. This allows you to test with external services such as Sendgrid, Stripe(later), Github(later), and more!&lt;/p&gt;
&lt;p&gt;Let’s get started with some setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Download and use &lt;a href=&quot;https://ngrok.com&quot;&gt;ngrok&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Download &lt;a href=&quot;https://ngrok.com&quot;&gt;ngrok&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Unzip and move the executable file to where you would like.&lt;/li&gt;
&lt;li&gt;In *nix OS’s, open ngrok with: &lt;code dir=&quot;auto&quot;&gt;path/to/ngrok HTTP start 3000&lt;/code&gt;. This will be dependent on the port you are using for your Rails server. ngrok will now fire up a tunnel service with a randomly generated URL.&lt;/li&gt;
&lt;li&gt;Add &lt;code dir=&quot;auto&quot;&gt;config.hosts &amp;#x3C;&amp;#x3C; &quot;yoursubdomain.ngrok.io&quot;&lt;/code&gt; to &lt;code dir=&quot;auto&quot;&gt;config/environments/development.rb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Optionally, if you upgrade to a paid version of &lt;code dir=&quot;auto&quot;&gt;ngrok&lt;/code&gt;, you can set a subdomain, so you do not have to change your settings elsewhere every time you restart &lt;code dir=&quot;auto&quot;&gt;ngrok&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Follow your domain’s DNS provider’s instructions for adding an MX record for the subdomain or domain name you have chosen to use for inbound email. The MX record will be &lt;code dir=&quot;auto&quot;&gt;mx.sendgrid.net&lt;/code&gt;, with a priority of &lt;code dir=&quot;auto&quot;&gt;10&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, before we head to the next part, we need to create and store a password for the incoming mail. You can use any method you like to create a password, and if you just can’t think of any way to do so, you can run &lt;code dir=&quot;auto&quot;&gt;SecureRandom.alphanumeric&lt;/code&gt; in Rails bash.&lt;/p&gt;
&lt;p&gt;Now, to store this password you just came up with, we will need to introduce a new small, but powerful, feature, &lt;code dir=&quot;auto&quot;&gt;Rails Credentials&lt;/code&gt;. Since Rails 5.2, Rails provides a built-in way for storing secure credentials, that are encrypted.&lt;/p&gt;
&lt;p&gt;To start using Rails Credentials, you will want to fire off the edit command:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;EDITOR&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;atom --wait&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;credentials:edit&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The editor I am using in this example is Atom. However, you can use any editor you like, and it’s command line caller. Also, &lt;code dir=&quot;auto&quot;&gt;--wait&lt;/code&gt; is used to make sure that the editor will wait for Rails to finish decrypting the file before opening it in the editor.&lt;/p&gt;
&lt;p&gt;Now that we have the encrypted file open, add the following lines:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;action_mailbox&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;ingress_password&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;password&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;you just made*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Save the file and close the tab/editor to make sure Rails saves the file and encrypts it. You will notice when Rails first opened the file, there was some output text in your terminal. Which includes the master key, DO NOT LOSE THIS KEY. It will need to be used a the only text in a &lt;code dir=&quot;auto&quot;&gt;config/master.key&lt;/code&gt; to be able to encrypt and decrypt the credentials.&lt;/p&gt;
&lt;p&gt;Now, with that out of the way, there is a little bit more setup we need to do for Action Mailbox.&lt;/p&gt;
&lt;p&gt;First, two commands to get Rails and some tables set up for inbound email:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;action_mailbox:install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Lastly, add the following line to the &lt;code dir=&quot;auto&quot;&gt;config/application.rb&lt;/code&gt; file to let Rails know we’re using Sendgrid:&lt;/p&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;config.action_mailbox.ingress = :sendgrid&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Ok, back to Sendgrid’s setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Sendgrid, go to the &lt;a href=&quot;https://app.sendgrid.com/settings/parse&quot;&gt;Inbound Parse&lt;/a&gt; link, under settings, to create the email route that will send the email. Enter the following settings:
&lt;ul&gt;
&lt;li&gt;Enter &lt;code dir=&quot;auto&quot;&gt;app.yourdomain.com&lt;/code&gt; for the domain field. You can optionally choose an additional subdomain to receive emails within the Inbound Parse.&lt;/li&gt;
&lt;li&gt;Enter &lt;code dir=&quot;auto&quot;&gt;http://actionmailbox:THEPASSWORDFROMBEFORE@yoursubdomain.ngrok.io/rails/action_mailbox/sendgrid/inbound_emails&lt;/code&gt; as the destination URL.&lt;/li&gt;
&lt;li&gt;Check &lt;code dir=&quot;auto&quot;&gt;POST the raw, full MIME message&lt;/code&gt;, before pressing the &lt;code dir=&quot;auto&quot;&gt;submit&lt;/code&gt; button.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ok, we’re getting nearly done with the setup here. Next, we will need to set up the applications inbound email route and run a command to generate that mailbox class. First, in the &lt;code dir=&quot;auto&quot;&gt;application_mailbox.rb&lt;/code&gt; file that was created just a few commands ago, you will add &lt;code dir=&quot;auto&quot;&gt;routing /standups\.[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}@/i =&gt; :standups&lt;/code&gt;. This says when an incoming mail matches that email address syntax, it will be routed to the &lt;code dir=&quot;auto&quot;&gt;standups&lt;/code&gt; mailbox class for processing.&lt;/p&gt;
&lt;p&gt;After that, to create the mailbox file, you will just need to run a Rails generator:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;generate&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mailbox&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;forwards&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;A few changes will be needed to allow Action Mailbox and its files to capture text from incoming replies.&lt;/p&gt;
&lt;p&gt;First, we will update the &lt;code dir=&quot;auto&quot;&gt;EmailReminderMailer&lt;/code&gt; to create a unique reply-to address and include that email address as part of the outgoing email:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;EmailReminderMailer&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationMailer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reminder_email&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;team&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; user&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@team&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; team&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;reply_to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&apos;Standups App&apos; &amp;#x3C;standups.&lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;@app.yourdomain.com&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;make_bootstrap_mail(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;@user&lt;/span&gt;&lt;span&gt;.email,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;subject&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;team.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; Standup Reminder!&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;reply_to&lt;/span&gt;&lt;span&gt;: reply_to&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Here we are adding the &lt;code dir=&quot;auto&quot;&gt;reply_to&lt;/code&gt; string to so when someone replies to a reminder email, the reply will be routed through Sendgrid, to Action Mailbox, and then finally by the Standup Mailbox class.&lt;/p&gt;
&lt;p&gt;Next, we will update the mailer template to have &lt;code dir=&quot;auto&quot;&gt;##- Please type your reply above this line -##&lt;/code&gt; and some text letting the email recipient know they can add a standup by replying:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-secondary text-center&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;font-size:8px&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;##- Please type your reply above this line -##&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-center mb-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Standup App&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-center mb-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@team&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; Reminder! &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mb-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Just wanted to remind you to add your standup for&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;the team: &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;@team&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; link_to &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Add Your Standup&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;new_standup_url()&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btn btn-primary btn-lg mx-auto mt-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;width:95%&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Lastly, we will need to add an extra column to the &lt;code dir=&quot;auto&quot;&gt;Standups&lt;/code&gt; table to track the &lt;code dir=&quot;auto&quot;&gt;Message-ID&lt;/code&gt; coming from the Mailgun routed emails. As you can not count on an email service to provide “just once” delivery, we will need to track these unique IDs ourselves on the Standup table.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails g migration AddMessageIdToStandups message_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next, before the end of the newly created migrations &lt;code dir=&quot;auto&quot;&gt;change&lt;/code&gt; method, you will want to add &lt;code dir=&quot;auto&quot;&gt;add_index :standups, :message_id&lt;/code&gt;. This index will allow quick lookups as the &lt;code dir=&quot;auto&quot;&gt;Standups&lt;/code&gt; table grows. Finally, migrate the actual change:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Lastly, to make sure sidekiq picks up the queues that Rails uses for Action Mailbox, we will need to adjust the &lt;code dir=&quot;auto&quot;&gt;worker&lt;/code&gt; in the &lt;code dir=&quot;auto&quot;&gt;Procfile.dev&lt;/code&gt; to:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;worker: bundle exec sidekiq -q default -q mailers -q action_mailbox_routing -q active_storage_analysis&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With those changes out of the way we can now add the new &lt;code dir=&quot;auto&quot;&gt;StandupsMailbox&lt;/code&gt; class that will parse the incoming email:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StandupsMailbox&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationMailbox&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;TASK_TYPE_HASH&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;[d]&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Did&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;[t]&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Todo&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;[b]&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Blocker&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Get a user id from reply-to or bail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;reply_user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mail.&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;last&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;last&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; reply_user.&lt;/span&gt;&lt;span&gt;blank?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Find a user by the id or bail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; reply_user)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; user.&lt;/span&gt;&lt;span&gt;nil?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Bail if standup with incoming message-id exists&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standup&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exists?&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;message_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; inbound_email.&lt;/span&gt;&lt;span&gt;message_id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Bail if a standup for today exists&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;today&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;today&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;iso8601&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standup&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exists?&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;standup_date&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; today)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Get content or bail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;safe_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Html&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;WhiteListSanitizer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sanitize&lt;/span&gt;&lt;span&gt;(mail_body)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;tasks_from_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; safe_body.&lt;/span&gt;&lt;span&gt;scan&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/(&lt;/span&gt;&lt;span&gt;\[&lt;/span&gt;&lt;span&gt;[dtb]{1}&lt;/span&gt;&lt;span&gt;\]&lt;/span&gt;&lt;span&gt;.*)$/&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; tasks_from_body.&lt;/span&gt;&lt;span&gt;blank?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; tasks_from_body.&lt;/span&gt;&lt;span&gt;empty?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;build_and_create_standup(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;: user,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;: tasks_from_body,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;: today,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;message_id&lt;/span&gt;&lt;span&gt;: inbound_email.message_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build_and_create_standup&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user:&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;tasks:&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;date:&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message_id:&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;standup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standup&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;standup_date&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; date,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;message_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; message_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tasks.&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;task_type, task_body &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; task.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scan&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/(&lt;/span&gt;&lt;span&gt;\[&lt;/span&gt;&lt;span&gt;[dtb]&lt;/span&gt;&lt;span&gt;\]&lt;/span&gt;&lt;span&gt;)(.*)$/&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;standup.&lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TASK_TYPE_HASH&lt;/span&gt;&lt;span&gt;[task_type], &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; task_body)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;standup.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mail_body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@mail_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;begin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; mail.&lt;/span&gt;&lt;span&gt;multipart?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mail.&lt;/span&gt;&lt;span&gt;parts&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decoded&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mail.&lt;/span&gt;&lt;span&gt;decoded&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The class is relatively simple, but let’s go over it section by section:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StandupsMailbox&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationMailbox&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;TASK_TYPE_HASH&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;[d]&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Did&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;[t]&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Todo&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;[b]&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; =&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Blocker&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Here we are setting up the &lt;code dir=&quot;auto&quot;&gt;StandupsMailbox&lt;/code&gt; to inherit from the &lt;code dir=&quot;auto&quot;&gt;ApplicationMailbox&lt;/code&gt; class, which handles the routing. Additionally, we are creating a hash to later use in the text content to &lt;code dir=&quot;auto&quot;&gt;Task&lt;/code&gt; type conversion.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;process&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Get a user id from reply-to or bail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;reply_user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mail.&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;last&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;@&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&amp;#x26;.&lt;/span&gt;&lt;span&gt;last&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; reply_user.&lt;/span&gt;&lt;span&gt;blank?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Find a user by the id or bail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; reply_user)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; user.&lt;/span&gt;&lt;span&gt;nil?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Bail if standup with incoming message-id exists&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standup&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exists?&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;message_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; inbound_email.&lt;/span&gt;&lt;span&gt;message_id&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Bail if a standup for today exists&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;today&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;today&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;iso8601&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standup&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exists?&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;standup_date&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; today)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# Get content or bail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;safe_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Html&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;WhiteListSanitizer&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;sanitize&lt;/span&gt;&lt;span&gt;(mail_body)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tasks_from_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; safe_body.&lt;/span&gt;&lt;span&gt;scan&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/(&lt;/span&gt;&lt;span&gt;\[&lt;/span&gt;&lt;span&gt;[dtb]{1}&lt;/span&gt;&lt;span&gt;\]&lt;/span&gt;&lt;span&gt;.*)$/&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; tasks_from_body.&lt;/span&gt;&lt;span&gt;blank?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; tasks_from_body.&lt;/span&gt;&lt;span&gt;empty?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Here we are grabbing some information used in the parsing, as well as giving the &lt;code dir=&quot;auto&quot;&gt;process&lt;/code&gt; method chances to exit early if the incoming email is not sufficient for processing and &lt;code dir=&quot;auto&quot;&gt;Standup&lt;/code&gt; creation. In the first section, the incoming email address is parsed to find the user’s &lt;code dir=&quot;auto&quot;&gt;id&lt;/code&gt;. That string is then used to find a User. If there is no user, the method returns without adding a Standup.&lt;/p&gt;
&lt;p&gt;The next line will exit the method early if there is already a standup with the current Message-ID. Again, this is making sure to guard against email providers, not guaranteeing “just once” delivery. That is followed up by generating a variable for the current date and making sure there is no standup with the &lt;code dir=&quot;auto&quot;&gt;current_user&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;current_date&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To get the mail’s content, we will want to do two things, make sure we get the correct body from the mail object as email clients can send both an HTML and plain text version. Then we will want to make sure we only grab the content above the &lt;code dir=&quot;auto&quot;&gt;##- Please type your reply above this line -##&lt;/code&gt; string by using the &lt;code dir=&quot;auto&quot;&gt;split&lt;/code&gt; method (which splits a string into as many parts based on the separator specified in the argument):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mail_body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@mail_body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;begin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; mail.&lt;/span&gt;&lt;span&gt;multipart?&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mail.&lt;/span&gt;&lt;span&gt;parts&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;decoded&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;mail.&lt;/span&gt;&lt;span&gt;decoded&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;##- Please type your reply above this line -##&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Finally, the actual email content is parsed with a regular expression. Regular Expression is a programming language that allows you to pattern match on a string and even capture parts of the pattern matching. This particular pattern(which you can get a more thorough syntax explanation &lt;a href=&quot;https://regex101.com/r/Bg9EIy/2&quot;&gt;here&lt;/a&gt;) searches for lines that begin with &lt;code dir=&quot;auto&quot;&gt;[d]&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;[t]&lt;/code&gt; or &lt;code dir=&quot;auto&quot;&gt;[r]&lt;/code&gt;. If those are present, it captures the content to the end of the line. The &lt;code dir=&quot;auto&quot;&gt;.scan&lt;/code&gt; method on the content’s body allows it to catch all occurrences of the above pattern. If the scan’s output is empty, the &lt;code dir=&quot;auto&quot;&gt;process&lt;/code&gt; method exits.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;build_and_create_standup(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;: user,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;: tasks_from_body,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;: today,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;message_id&lt;/span&gt;&lt;span&gt;: email.headers&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Message-ID&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build_and_create_standup&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;user:&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;tasks:&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;date:&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;message_id:&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;standup&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Standup&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;user_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;standup_date&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; date,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;message_id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; message_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tasks.&lt;/span&gt;&lt;span&gt;each&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;task_type, task_body &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; task.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;scan&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/(&lt;/span&gt;&lt;span&gt;\[&lt;/span&gt;&lt;span&gt;[dtb]&lt;/span&gt;&lt;span&gt;\]&lt;/span&gt;&lt;span&gt;)(.*)$/&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;flatten&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;standup.&lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Task&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TASK_TYPE_HASH&lt;/span&gt;&lt;span&gt;[task_type], &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; task_body)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;standup.&lt;/span&gt;&lt;span&gt;save&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The last section here is a culmination of all of the stored information so far to be saved into a new &lt;code dir=&quot;auto&quot;&gt;Standup&lt;/code&gt;. The &lt;code dir=&quot;auto&quot;&gt;user&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;tasks_from_body&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;today&lt;/code&gt;, and &lt;code dir=&quot;auto&quot;&gt;message_id&lt;/code&gt; are all passed into a method that will hand the actual save. The &lt;code dir=&quot;auto&quot;&gt;build_and_create_standup&lt;/code&gt; method creates a new &lt;code dir=&quot;auto&quot;&gt;Standup&lt;/code&gt;, with the user’s ID, date, and &lt;code dir=&quot;auto&quot;&gt;message_id&lt;/code&gt;. Once the object is created, the task’s strings are iterated over to build the &lt;code dir=&quot;auto&quot;&gt;Task&lt;/code&gt; with a type and assigned as child objects with the &lt;code dir=&quot;auto&quot;&gt;&amp;#x3C;&amp;#x3C;&lt;/code&gt; syntax. Finally the new &lt;code dir=&quot;auto&quot;&gt;Standup&lt;/code&gt; object with children &lt;code dir=&quot;auto&quot;&gt;Tasks&lt;/code&gt; will be saved with &lt;code dir=&quot;auto&quot;&gt;standup.save&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Lastly, you can test this all works if you reply back to an email(that was sent through Sendgrid SMTP and not letter_opener) with the following text:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[d] Did a thing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[d] And Another&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[t] Something to do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[b] Something in the way. Some really long line about something or another&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Testing this will require a new spec file with a few &lt;code dir=&quot;auto&quot;&gt;it&lt;/code&gt; blocks to test all the branches the &lt;code dir=&quot;auto&quot;&gt;StandupMailbox&lt;/code&gt; may encounter.&lt;/p&gt;
&lt;p&gt;First, we will need to add one small change to the &lt;code dir=&quot;auto&quot;&gt;rails_helper.rb&lt;/code&gt; file to make sure RSpec has access to the Rails Action Mailbox test helpers. I’ll add the new lines and a few lines above each, so you know where to put the new stuff:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;support/system_macros&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Warden&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Test&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Helpers&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;action_mailbox/test_helper&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# &amp;#x3C;-- new line&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RSpec&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;configure&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config.&lt;/span&gt;&lt;span&gt;include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Devise&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Test&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;ControllerHelpers&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;controller&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config.&lt;/span&gt;&lt;span&gt;include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Devise&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Test&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;ControllerHelpers&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;config.&lt;/span&gt;&lt;span&gt;include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ActionMailbox&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;TestHelper&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;mailbox&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;# &amp;#x3C;-- new line&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;…and now the actual spec file itself:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;rails_helper&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;include&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ActiveJob&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;TestHelper&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RSpec&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;StandupsMailbox&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;mailbox&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;let(:&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;FactoryBot&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt;) }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;let(:&lt;/span&gt;&lt;span&gt;to_email&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;standups.&lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;user.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;@app.buildasaasappinrails.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;let(:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;%Q(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[d] Did a thing&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[d] And Another&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[t] Something to do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;[b] Something in the way. Some really long line about something or another&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;##- Please type your reply above this line -##&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;subject &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;receive_inbound_email_from_mail(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;: user.email,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: to_email,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;subject&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Re: Reminder&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;: body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;before &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ActiveJob&lt;/span&gt;&lt;span&gt;::&lt;/span&gt;&lt;span&gt;Base&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;queue_adapter&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;it &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;saves standup record&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expect { subject }.&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change(Standup, :&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;it &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;saves task records&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expect { subject }.&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change(Task, :&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;fails on bad to email&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;let(:&lt;/span&gt;&lt;span&gt;to_email&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;standups@app.buildasaasappinrails.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;it &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;does not save standup record&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expect { subject &lt;/span&gt;&lt;span&gt;rescue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt; }.&lt;/span&gt;&lt;span&gt;to_not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change(Standup, :&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;fails on no user&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;let(:&lt;/span&gt;&lt;span&gt;to_email&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;standups.9a859af8-30ca-4473-b073-47105352d936@app.buildasaasappinrails.com&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;it &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;does not save standup record&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expect { subject &lt;/span&gt;&lt;span&gt;rescue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt; }.&lt;/span&gt;&lt;span&gt;to_not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change(Standup, :&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;fails on useless body&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;let(:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;asdj;oisaduaskd&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;it &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;does not save standup record&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expect { subject &lt;/span&gt;&lt;span&gt;rescue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt; }.&lt;/span&gt;&lt;span&gt;to_not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change(Standup, :&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;context &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;fails on empty body&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;let(:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;it &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;does not save standup record&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expect { subject &lt;/span&gt;&lt;span&gt;rescue&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt; }.&lt;/span&gt;&lt;span&gt;to_not&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change(Standup, :&lt;/span&gt;&lt;span&gt;count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;While long and containing six examples, this spec is actually pretty straightforward. It is first testing the happy path where everything is set up and working. Then checks each failing path that doesn’t create a standup, in order as those paths appear in the &lt;code dir=&quot;auto&quot;&gt;StandupsMailbox&lt;/code&gt;’s &lt;code dir=&quot;auto&quot;&gt;.process&lt;/code&gt; method.&lt;/p&gt;
</content:encoded><category>Rails</category><category>ActionMailbox</category></item><item><title>How to inspect a Ruby Object</title><link>https://robrace.dev/blog/how-to-inspect-a-ruby-object/</link><guid isPermaLink="true">https://robrace.dev/blog/how-to-inspect-a-ruby-object/</guid><description>Tip to inspect a Ruby Object</description><pubDate>Sun, 31 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Imagine this, you adding a new object in your view, you reload the page, and you are greeted with &lt;code dir=&quot;auto&quot;&gt;Undefined method for nil:NilClass&lt;/code&gt;. “Ok, I’ll just print the object”, you think to yourself:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AwesomeObject&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#&amp;#x3C;AwesomeObject:0x00007fd9a142a748&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Well, that didn’t help. Let’s go over the ways to print and inspect Ruby objects.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;printing-an-object&quot;&gt;Printing an Object&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;First, what happens when you print an object?&lt;/p&gt;
&lt;p&gt;When you &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; an object instance it will call &lt;code dir=&quot;auto&quot;&gt;.to_s&lt;/code&gt; method on that object. In the cases of built-in Object types in Ruby, this is handled for you. Such as when you &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; a number:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;7&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;=&gt; &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When you do that on an object, like above, the default behavior of &lt;code dir=&quot;auto&quot;&gt;to_s&lt;/code&gt; on an object is to print the Object’s class name (this part &lt;code dir=&quot;auto&quot;&gt;AwesomeObject&lt;/code&gt;) and the &lt;code dir=&quot;auto&quot;&gt;object_id&lt;/code&gt; (this part &lt;code dir=&quot;auto&quot;&gt;0x00007fd9a142a748&gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;We can update the class to include a &lt;code dir=&quot;auto&quot;&gt;to_s&lt;/code&gt; to control the output of an object’s instance in a &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; statement:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AwesomeObject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;to_s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;This is some awesome output&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then, once you run &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; on it, the output from the &lt;code dir=&quot;auto&quot;&gt;to_s&lt;/code&gt; method will be used:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AwesomeObject&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;This&lt;/span&gt;&lt;span&gt; is some awesome output&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;inspecting-an-object&quot;&gt;Inspecting an Object&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Another method defined on all objects and usually overridden by the specific uses-cases of the individual classes is the &lt;code dir=&quot;auto&quot;&gt;inspect&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Let’s make out &lt;code dir=&quot;auto&quot;&gt;AwesomeObject&lt;/code&gt; a little more complicated:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AwesomeObject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@age&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; age&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;attr_reader&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;to_s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;This is some awesome output: &lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To go back over the &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; which uses &lt;code dir=&quot;auto&quot;&gt;to_s&lt;/code&gt; and is customized in the class above:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;puts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AwesomeObject&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;42&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;This&lt;/span&gt;&lt;span&gt; is some awesome &lt;/span&gt;&lt;span&gt;output&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;42&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, by default, the inspect method will print out the class name and &lt;code dir=&quot;auto&quot;&gt;object_id&lt;/code&gt;, much like the build in &lt;code dir=&quot;auto&quot;&gt;to_s&lt;/code&gt; method on the &lt;code dir=&quot;auto&quot;&gt;Object&lt;/code&gt; class. Then it will display the attributes/instance methods on the object. This it would display the &lt;code dir=&quot;auto&quot;&gt;name&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;age&lt;/code&gt;, like so:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;AwesomeObject&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;42&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;inspect&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#&amp;#x3C;AwesomeObject:0x00007fd9a15720b0 @name=&quot;Rob&quot;, @age=42&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Just like the &lt;code dir=&quot;auto&quot;&gt;to_s&lt;/code&gt; method used in &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt;, you can also customize and override the &lt;code dir=&quot;auto&quot;&gt;inspect&lt;/code&gt; method in your own class. Imagine we used the following &lt;code dir=&quot;auto&quot;&gt;inspect&lt;/code&gt; method and called &lt;code dir=&quot;auto&quot;&gt;inspect&lt;/code&gt; on the object’s instance:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;AwesomeObject&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;initialize&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@age&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; age&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;attr_reader&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;inspect&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;This is some awesome output: &lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;#{&lt;/span&gt;&lt;span&gt;age&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Then, in your IRB or console, you can run &lt;code dir=&quot;auto&quot;&gt;.inspect&lt;/code&gt; on the instance:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;AwesomeObject&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Rob&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;42&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;inspect&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;This is some awesome output: Rob, 42&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;There you have it! Two ways to get the data from an object and how you can override them! I implore you to use &lt;code dir=&quot;auto&quot;&gt;puts&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;.inspect&lt;/code&gt; on the objects you may use daily, such as ActiveRecord objects, instances from gems, or clients such as HTTP clients or API clients. You will usually be able to see relevant information to debug any issue you come across.&lt;/p&gt;
</content:encoded><category>Ruby</category></item><item><title>Integrating Bootstrap 4 into Rails 6</title><link>https://robrace.dev/blog/integrating-bootstrap-4-in-rails-6/</link><guid isPermaLink="true">https://robrace.dev/blog/integrating-bootstrap-4-in-rails-6/</guid><description>Adding Bootstrap 4 and how to use a theme</description><pubDate>Wed, 27 May 2020 19:00:08 GMT</pubDate><content:encoded>&lt;p&gt;We’ve all been there, you start building a new Rails application and you realize there is no style on any of your generic Rails pages. So you know in the back of your head you can grab Bootstrap and an Admin theme to get things moving. You may then find yourself asking, “What’s the best way to get Bootstrap into Rails?” and “How do I add the theme itself to the application?”. Let’s did into those questions and more in this post!&lt;/p&gt;
&lt;p&gt;To install the a Bootstrap theme, we are going to be adding some packages, updating some configuration and adding a few additional gems/libraries to make life developing easier.&lt;/p&gt;
&lt;p&gt;The first thing to do is to add another version manager! Since we are using Yarn/NPM/Node, it is a good idea to manage the version of Node a particular application or directory will be using. To do so, we will use NVM, and you can install NVM with the following line. If you are one to look at the github repo or the install scripts, &lt;a href=&quot;https://github.com/nvm-sh/nvm&quot;&gt;you can here&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-o-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That line grabs the install script from Github and runs it as &lt;code dir=&quot;auto&quot;&gt;Bash&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now the current LTS (Long Term Stable) version of Node is version 12. LTS versions in the OSS (Open Source) communities is the version in which you can rely on long term stability. To install Node use the following command:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;nvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This line installs the latest updated version of version 10. Meaning, as small updates and bugfixes are released to the version 10 pipeline, they bump the version numbers under 12 (i.e., 12.3.4 -&gt; 12.3.5). Once installed, you can tell your terminal window/pane to use node 12 with the following command:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;nvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;When you created your new app, Rails should have made sure &lt;code dir=&quot;auto&quot;&gt;brew&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;yarn&lt;/code&gt; were added. Thus, you should now be able to install AdminLTE, Bootstrap, and a few other dependencies needed to get AdminLTE up and running in your application.&lt;/p&gt;
&lt;p&gt;First, to install a version of AdminLTE that is compatible with Bootstrap 4, we will need to install through Yarn on a specific Github branch&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;admin-lte@3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This command is saying, install &lt;code dir=&quot;auto&quot;&gt;admin-lte&lt;/code&gt; and use the version &lt;code dir=&quot;auto&quot;&gt;3&lt;/code&gt;. Next, we also install the packages for Bootstrap, jQuery and Popper.js to make sure they are available to the Rails application:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bootstrap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jquery&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;popper.js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That should take care of making sure all of the packages we need for the theme are there, but it seems as though Rails does not entirely install &lt;em&gt;everything&lt;/em&gt; needed for Webpacker to get up and running. To remedy this issue, let’s run the Webpacker install.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;webpacker:install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Alright, we are getting close to being done with setup! However, we should digress a bit in regards to Webpacker.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;h2 id=&quot;webpacker&quot;&gt;Webpacker&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Webpacker is the Ruby (Rails mostly) wrapper gem around the Javascript tool, Webpack. Webpack is a tool that allows you to pre-process, bundle and use ES6 like syntax inside Javascript. Webpacker then takes all of those features and starts to merge the ES6 Javascript world with the Asset pipeline for CSS and other more static assets.&lt;/p&gt;
&lt;p&gt;Webpacker introduces a concept of &lt;em&gt;“packs”&lt;/em&gt; which are ES6 syntax Javascript files that you can then include in any template with a &lt;code dir=&quot;auto&quot;&gt;pack_tag&lt;/code&gt; helper. Think of packs as entry points of javascript files into specific spots in a Rails app.&lt;/p&gt;
&lt;p&gt;The most basic pattern is the &lt;code dir=&quot;auto&quot;&gt;/app/javascript/application.js&lt;/code&gt; pack file, which is then included in the applications’ layout file by default.&lt;/p&gt;
&lt;p&gt;As your application grows, using packs to specify specific libraries and functionality within specific templates or sections of your application helps make sure your Javascript stays modular and compartmentalized.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h4 id=&quot;running-a-webpack-dev-server&quot;&gt;Running A Webpack Dev Server&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;If you have used Heroku or other hosting vendors, you may see reference to &lt;code dir=&quot;auto&quot;&gt;Procfile&lt;/code&gt;. The simplest definition of a Procfile is that it allows you to specify a list of different process types and lets another system(Heroku, Foreman gem, etc.) handle startup and shutdown. Allow all of that to be dealt with one line and one terminal session/tab/pane.&lt;/p&gt;
&lt;p&gt;When developing locally, the easiest step to take is to use the gem &lt;code dir=&quot;auto&quot;&gt;Foreman&lt;/code&gt;. To add this gem, add it to the development group in your &lt;code dir=&quot;auto&quot;&gt;Gemfile&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;bundle install&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;group &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;development&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gem &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;foreman&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Once added, you can add your &lt;code dir=&quot;auto&quot;&gt;Procfile&lt;/code&gt; to the root of your app’s folder structure. Meaning the same level as your &lt;code dir=&quot;auto&quot;&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;web:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bundle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;puma&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;5:5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; ${&lt;/span&gt;&lt;span&gt;PORT&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;3000&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;-e&lt;/span&gt;&lt;span&gt; ${&lt;/span&gt;&lt;span&gt;RACK_ENV&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;development&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;You may notice that instead of &lt;code dir=&quot;auto&quot;&gt;rails s&lt;/code&gt;, you see &lt;code dir=&quot;auto&quot;&gt;bundle exec puma&lt;/code&gt;. This change is due to how you would want to run Puma directly in most environments.&lt;/p&gt;
&lt;p&gt;If you were to start your environment with Foreman and this procfile, you would use the following on the command line:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;foreman&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That command starts up a process for each line in your Procfile. Thus, in this case, a process for Puma, to run the Rails server. However, if you visit your instance of the app, first you may notice it is now at &lt;a href=&quot;http://localhost:5000&quot;&gt;http://localhost:5000&lt;/a&gt;, and it is also not outputting development logs.&lt;/p&gt;
&lt;p&gt;So, how can we make development use &lt;code dir=&quot;auto&quot;&gt;rails s&lt;/code&gt; to keep the fancy logs? We can
create a secondary &lt;code dir=&quot;auto&quot;&gt;Procfile&lt;/code&gt; that is utilized just for development.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;web:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bin/rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; ${&lt;/span&gt;&lt;span&gt;PORT&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;3000&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;webpacker:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;./bin/webpack-dev-server&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;h2 id=&quot;what-is-webpack-dev-server&quot;&gt;What is &lt;code dir=&quot;auto&quot;&gt;webpack-dev-server&lt;/code&gt;?&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Very simply, it is a server that runs in the background that allows you to develop your Javascript, handles automatic reloads and makes sure packs are served to you your development Rails server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To get this Procfile running instead, you simply just need to add a field to your command line syntax:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;foreman&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Procfile.dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We’re getting closer! Now if you visit &lt;a href=&quot;http://localhost:5000&quot;&gt;http://localhost:5000&lt;/a&gt;, you should see the output in the terminal from your request. However, we’re still on port &lt;code dir=&quot;auto&quot;&gt;5000&lt;/code&gt;. This problem is easy to fix, just supply the command line with the environment variable used in the command to choose the port.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;PORT&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;3000&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;foreman&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Procfile.dev&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, you can quickly start everything up and get it running in one terminal tab.&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;final-webpacker-setup&quot;&gt;Final Webpacker Setup&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;Ok, we’re getting so close to getting everything up and running. We’re down to adding just a few additions to the Webpack javascript configuration.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;config/webpack/environment.js&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const { &lt;/span&gt;&lt;span&gt;environment&lt;/span&gt;&lt;span&gt; } = &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@rails/webpacker&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;webpack&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;webpack&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Add an additional plugin of your choosing : ProvidePlugin&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;environment&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;plugins&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;prepend&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Provide&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;webpack&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;ProvidePlugin&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;$: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;jquery&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;jQuery: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;jquery&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;jquery: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;jquery&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;window.Tether&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tether&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Popper: [&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;popper.js&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;// for Bootstrap 4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;environment&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;One of the additions are the 8 lines of plugin code that is used to make sure jQuery, Popper.js and Tether are available throughout the bundled javascript.&lt;/p&gt;
&lt;div&gt;&lt;h4 id=&quot;the-application-pack&quot;&gt;The Application Pack&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;When adding Javascript to a Rails application, there are javascript libraries, setup or variables you want throughout the application. In the world of Rails 6 and Webpacker, these items go in the Application Pack, located at &lt;code dir=&quot;auto&quot;&gt;app/javascript/packs/application.js&lt;/code&gt;. The good news is that this file is created when you used the Rails command to create a new Rails app. Let’s go over the additions to get AdminLTE’s needs fulfilled.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// This file is automatically compiled by Webpack, along with any other files&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// present in this directory. You&apos;re encouraged to place your actual application logic in&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// a relevant structure within app/javascript and only use these pack files to reference&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// that code so it&apos;ll be compiled.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@rails/ujs&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;turbolinks&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;@rails/activestorage&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;channels&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;var &lt;/span&gt;&lt;span&gt;jQuery&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;jquery&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// import jQuery from &quot;jquery&quot;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;global&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;global&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;jQuery&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jQuery&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;window&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;jQuery&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;jQuery&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bootstrap&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;admin-lte&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// Uncomment to copy all static images under ../images to the output folder and reference&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// them with the image_pack_tag helper in views (e.g &amp;#x3C;%= image_pack_tag &apos;rails.png&apos; %&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// or the `imagePath` JavaScript helper below.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;//&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// const images = require.context(&apos;../images&apos;, true)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;// const imagePath = (name) =&gt; images(name, true)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The commented code and the first four lines of &lt;code dir=&quot;auto&quot;&gt;require&lt;/code&gt;s are added by default. These are libraries or packages that Rails needs for most applications. The new lines are adding jQuery and variables that other packages or libraries may expect to exist. Lastly, &lt;code dir=&quot;auto&quot;&gt;bootstrap&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;admin-lte&lt;/code&gt; are required to make available to the entire Rails application.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;h2 id=&quot;why-require-and-not-import&quot;&gt;Why &lt;code dir=&quot;auto&quot;&gt;require&lt;/code&gt; and not &lt;code dir=&quot;auto&quot;&gt;import&lt;/code&gt;&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;If you have done any recent javascript development, you may question why we are using &lt;code dir=&quot;auto&quot;&gt;require&lt;/code&gt; and not &lt;code dir=&quot;auto&quot;&gt;import&lt;/code&gt;. The answer here is rather simple. Based on the packages we are using and adding in, they expect other’s to have loaded first. Most specifically, &lt;code dir=&quot;auto&quot;&gt;admin-lte&lt;/code&gt; expects the global variable &lt;code dir=&quot;auto&quot;&gt;jQuery&lt;/code&gt; to exist when it loads. Using &lt;code dir=&quot;auto&quot;&gt;require&lt;/code&gt; guarantees code is executed in the order of the file, whereas &lt;code dir=&quot;auto&quot;&gt;import&lt;/code&gt; usually is pre-processed and executed out of order.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div&gt;&lt;h4 id=&quot;the-application-stylesheet&quot;&gt;The Application Stylesheet&lt;/h4&gt;&lt;/div&gt;
&lt;p&gt;Lastly, we need to add some imports to the bottom of the automatically created Application Stylesheet. If you have done any Rails development in the last few years, you may have interacted with the Asset Pipeline, which is the way Rails handled the inclusion of javascript, stylesheets and any other static assets.&lt;/p&gt;
&lt;p&gt;You’ll notice that the name of the file is different than what was generated by Rails. For &lt;code dir=&quot;auto&quot;&gt;import&lt;/code&gt; the extension &lt;code dir=&quot;auto&quot;&gt;scss&lt;/code&gt; needs to be used, signaling the Asset Pipeline to run the file as Sass, then CSS.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* This is a manifest file that&apos;ll be compiled into application.css, which will include all the files&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* listed below.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin&apos;s&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* vendor/assets/stylesheets directory can be referenced here using a relative path.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* You&apos;re free to add application-wide styles to this file and they&apos;ll appear at the bottom of the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* files in this directory. Styles in this file should be added after the last require_* statement.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;* It is generally better to create a new file per style scope.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*= require_tree .&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*= require_self&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700&amp;#x26;lang=en&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;bootstrap&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;@import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;admin-lte/dist/css/adminlte&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;In Sass CSS, you can include other stylesheets or fonts by using the &lt;code dir=&quot;auto&quot;&gt;@import&lt;/code&gt; syntax. In this stylesheet above, we are adding a font from Google Fonts that are referenced in AdminLTE’s stylesheets. Next, we include stylesheets for Bootstrap and AdminLTE.&lt;/p&gt;
&lt;p&gt;In the following section, we are going to add some Controllers and Templates to added the necessary markup to structure the application.&lt;/p&gt;
&lt;p&gt;Before we begin implementing the layout, we need to apply a route and controller to bypass the default welcome page. We also will be using this controller in the application as we go along.&lt;/p&gt;
&lt;p&gt;Let’s also pause to mention that Rails comes with built-in command line generators to generate controllers, models, mailers. As well as generating database migration file or even generating a scaffold of all of those combined. In our case, let’s focus on the controller generator. The general format for this generator is:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails generate controller Activity mine feed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;It created a controller with empty methods for the actions we included in the command line. It created template files, test files, and other auxiliary files.&lt;/p&gt;
&lt;p&gt;Let’s make one quick edit to the route file to define a default route, adding “root to: ‘activity#mine’” to the bottom of the route file, as follows:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;activity/mine&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;activity/feed&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;activity#mine&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As we had set up the theme package, application javascript file and application stylesheet file earlier in the chapter, we can get into writing the markup needed to implement this theme rather quickly.&lt;/p&gt;
&lt;p&gt;First, we can modify the main layout file to include the necessary elements and classes to wrap the content that will be provided by routed templates.&lt;/p&gt;
&lt;p&gt;File: &lt;code dir=&quot;auto&quot;&gt;app/views/layouts/application.html.erb&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Some App&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csrf_meta_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csp_meta_tag &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; stylesheet_link_tag &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;data-turbolinks-track&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt;  javascript_pack_tag &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;data-turbolinks-track&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://kit.fontawesome.com/6d90f39943.js&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;-- Make your own FontAwesome Kit on their site --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sidebar-mini&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;wrapper&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;layouts/header&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;layouts/sidebar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;content-wrapper&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;layouts/footer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The details of this file are not all pertinent to the addition of the Bootstrap theme, so we’ll only touch on those that matter. First, a class is added to the body element, “body class=“sidebar-mini”“. Next, “&lt;/p&gt;&lt;div&gt;” and “&lt;div&gt;” are added as wrapping elements to the first the page and then the inner content. You may notice there is some included Ruby code in there as well. Those &lt;code dir=&quot;auto&quot;&gt;render partial:&lt;/code&gt; calls allow us to add markup from other template files, in that position. In this case, it adds various larger parts of the application template such as the header, sidebar or footer. Plus, it keeps the layout to only the most necessary foundational elements to build the page structure. Then, “&amp;#x3C;%= yield %&gt;” which is the Rails’ template method for adding the template from the controller and action called.&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Since we are using partial templates, we go ahead and create the header file next.&lt;/p&gt;
&lt;p&gt;One thing to note here is the use of the underscore in the template file’s name. In Rails, partial template files are designated with an underscore at the beginning of the file name. What are partials? Partials are simply a way to break down templates into smaller chunks. This approach comes into far greater play in situations such as looping over a collection of items. There, you would make the template chunk to display item information a partial. For example, the header template is titled &lt;code dir=&quot;auto&quot;&gt;_header.erb&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/layout/_header.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;main-header navbar navbar-expand navbar-white navbar-light border-bottom&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Left navbar links --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;navbar-nav&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-widget&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;pushmenu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-bars&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item d-none d-sm-inline-block&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;index3.html&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Home&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item d-none d-sm-inline-block&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Contact&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- SEARCH FORM --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-inline ml-3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;input-group input-group-sm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-control form-control-navbar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;placeholder&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Search&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;aria-label&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;Search&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;input-group-append&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;btn btn-navbar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;submit&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-search&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Right navbar links --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;navbar-nav ml-auto&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Messages Dropdown Menu --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item dropdown&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-toggle&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;far fa-comments&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;badge badge-danger navbar-badge&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-menu dropdown-menu-lg dropdown-menu-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Message Start --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;http://placehold.it/128x128&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;User Avatar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;img-size-50 mr-3 img-circle&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;media-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item-title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Brad Diesel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right text-sm text-danger&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-star&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-sm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Call me whenever you can...&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-sm text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;far fa-clock mr-1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; 4 Hours Ago&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Message End --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-divider&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Message Start --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;http://placehold.it/128x128&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;User Avatar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;img-size-50 img-circle mr-3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;media-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item-title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;John Pierce&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right text-sm text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-star&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-sm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;I got your message bro&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-sm text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;far fa-clock mr-1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; 4 Hours Ago&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Message End --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-divider&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Message Start --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;http://placehold.it/128x128&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;User Avatar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;img-size-50 img-circle mr-3&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;media-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item-title&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Nora Silvester&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right text-sm text-warning&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-star&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h3&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-sm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;The subject goes here&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;text-sm text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;far fa-clock mr-1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; 4 Hours Ago&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Message End --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-divider&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item dropdown-footer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;See All Messages&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Notifications Dropdown Menu --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item dropdown&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-toggle&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;far fa-bell&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;badge badge-warning navbar-badge&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-menu dropdown-menu-lg dropdown-menu-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item dropdown-header&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;15 Notifications&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-divider&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-envelope mr-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; 4 new messages&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right text-muted text-sm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;3 mins&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-divider&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-users mr-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; 8 friend requests&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right text-muted text-sm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;12 hours&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-divider&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-file mr-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; 3 new reports&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right text-muted text-sm&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;2 days&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-divider&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;dropdown-item dropdown-footer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;See All Notifications&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;data-widget&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;control-sidebar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;data-slide&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;fas fa-th-large&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This template contains the structure to have the app title/logo, dropdowns and any additional links you may need for navigation. To get the app template structure setup, we can leave in some of the demo data and elements to show how the dropdowns would look and work.&lt;/p&gt;
&lt;p&gt;The next partial to create is the &lt;code dir=&quot;auto&quot;&gt;_sidebar&lt;/code&gt; template:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/layouts/_sidebar.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;aside&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;main-sidebar sidebar-dark-primary elevation-4&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Brand Logo --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;index3.html&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;brand-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;brand-text font-weight-light&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Standup App&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;!-- Sidebar --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;slimScrollDiv&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;position: relative; overflow: hidden; width: auto; height: 652px;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sidebar&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;min-height: 652px; overflow: hidden; width: auto; height: 652px;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Sidebar user panel (optional) --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;user-panel mt-3 pb-3 mb-3 d-flex&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;img&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;http://placehold.it/160x160&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;img-circle elevation-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;alt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;User Image&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;info&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;d-block&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;User Name&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;!-- Sidebar Menu --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;mt-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav nav-pills nav-sidebar flex-column&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-widget&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;treeview&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;role&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;menu&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;data-accordion&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- Add icons to the links using the .nav-icon class&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;             &lt;/span&gt;&lt;/span&gt;&lt;span&gt;with font-awesome or any other icon font library --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item has-treeview menu-open&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link active&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-icon fas fa-tachometer-alt&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Activity&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;right fas fa-angle-left&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav nav-treeview&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;./index.html&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link active&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;far fa-circle nav-icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Mine&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-item&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;./index2.html&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;nav-link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;far fa-circle nav-icon&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Feed&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- /.sidebar-menu --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;!-- /.sidebar --&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;aside&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;As you see, the sidebar partial is a bit smaller and more straightforward. It contains a little bit of user information and links to other parts of the application that we change later.&lt;/p&gt;
&lt;p&gt;The last major piece of the layout is the footer. It is simple, and it is short. Again, it is another partial. Here is the ERB markup:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/layouts/_foooter.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;footer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;main-footer&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;strong&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Copyright © 2019 Your Stuff.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;strong&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;All rights reserved.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;footer&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now, to have all of this show up on a page, you can edit the root controller
action’s template to use the theme’s div classes&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;app/views/activity/mine.html.erb&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;content-header&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container-fluid&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;row mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;col-sm-6&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;m-0 text-dark&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;Some Page&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container-fluid&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;row&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;col-lg-12&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Stuff&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;span&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That’s about it to get set up with Bootstrap 4 and Rails 6.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;</content:encoded><category>Rails</category><category>Bootstrap</category></item><item><title>19 Must-Have Gems for 2020</title><link>https://robrace.dev/blog/must-have-gems-for-saas-2020/</link><guid isPermaLink="true">https://robrace.dev/blog/must-have-gems-for-saas-2020/</guid><description>19 Must-Have Gems for 2020</description><pubDate>Tue, 19 May 2020 19:00:08 GMT</pubDate><content:encoded>&lt;p&gt;An application can only be as good as the features it is providing for its end-users. Every developer toes the line between indiscriminately adding gems and building new functionality themselves. When time is of the essence, these are the gems I reach for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/basecamp/local_time&quot;&gt;local_time&lt;/a&gt;&lt;/strong&gt; - An relatively older gem and stable, but this is a gem that includes a view helper and javascript helper to display relative time (i.e. ‘updated 2 minutes ago’)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/plataformatec/devise&quot;&gt;devise&lt;/a&gt;&lt;/strong&gt; - Devise is still the go-to gem for authentication in most Rails apps. Its ease of use and existing features make it dead-simple to add authentication. For those looking for a simpler gem with a smaller footprint, &lt;a href=&quot;https://github.com/binarylogic/authlogic&quot;&gt;Authlogic&lt;/a&gt; gets a runner up in this list for authentication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/collectiveidea/interactor&quot;&gt;Interactor&lt;/a&gt;&lt;/strong&gt; - When you start to follow the Service Object pattern, adding Interactor can help guide your context and result of those objects through a common interface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/RolifyCommunity/rolify&quot;&gt;rolify&lt;/a&gt; and &lt;a href=&quot;https://github.com/CanCanCommunity/cancancan&quot;&gt;cancancan&lt;/a&gt;&lt;/strong&gt; - Rolify allows us to assign roles such as User or Admin then CanCanCan will enable us to take those roles and apply authorization policies. However, there is a case for &lt;a href=&quot;https://github.com/varvet/pundit&quot;&gt;Pundit&lt;/a&gt; for those who like the Policy Object approach’s simplicity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/ankane/lockbox&quot;&gt;Lockbox&lt;/a&gt;&lt;/strong&gt; - When you need to encrypt data in your database for compliance or even client security, Lockbox makes it very easy to do. Working with plenty of types out of the box, such as most database types, other Rails tools(ActiveStorage), and third-party gems(Shrine).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/mperham/sidekiq&quot;&gt;Sidekiq&lt;/a&gt;&lt;/strong&gt; - This is &lt;strong&gt;the gem&lt;/strong&gt; when it comes to running asynchronous tasks. Sidekiq is backed by Redis and has been long supported by the ActiveJob Rails abstractions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/kaminari/kaminari&quot;&gt;Kaminari&lt;/a&gt;&lt;/strong&gt; - A simple yet powerful approach to adding pagination into your Rails applications. In many places, it is as simple a few drop-in changes, but can be used to on anything from ActiveRecord collections to plain Arrays. It also has templates to be used in many of the most known CSS frameworks such as Bootstrap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/stuyam/bootstrap-email&quot;&gt;Bootstrap Email&lt;/a&gt;&lt;/strong&gt; - Take the guesswork out of email design and CSS inlining inside of Rails Mailers. This gem allows you to use Bootstrap-like HTML structures, use a Bootstrap like design, and inline the CSS to work in most email browser/apps. Quite the timesaver!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/RubyMoney/money-rails&quot;&gt;money-rails&lt;/a&gt;&lt;/strong&gt; - This will help standardize around common issues found when calculating money as well as providing a few template helpers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/MiniProfiler/rack-mini-profiler&quot;&gt;Rack Mini Profiler&lt;/a&gt;&lt;/strong&gt; - Everyone should know what their application is doing and where it is spending its time. This gem adds a profiler to the development pages and can include some nifty flame graphs when you really want to dig into the data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/rspec/rspec-rails&quot;&gt;rspec-rails&lt;/a&gt;, &lt;a href=&quot;https://github.com/thoughtbot/factory_bot_rails&quot;&gt;factory_bot_rails&lt;/a&gt;, &lt;a href=&quot;https://github.com/colszowka/simplecov&quot;&gt;simplecov&lt;/a&gt;&lt;/strong&gt; - I personally like to use RSpec over the default Rails test framework. Though, the community can be quite divisive on testing tools! With every test suite, I include FactoryBot to easily spin up objects for those specs and SimpleCov to dig into my test coverage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/ryanb/letter_opener&quot;&gt;letter_opener&lt;/a&gt;&lt;/strong&gt; - Still my #1 gem for working with email in a Rails app. In your local development, it catches the outgoing mail and opens in a browser, rendering it as it would an email.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/lostisland/faraday&quot;&gt;Faraday&lt;/a&gt; or &lt;a href=&quot;https://github.com/jnunemaker/httparty&quot;&gt;HTTParty&lt;/a&gt;&lt;/strong&gt; - When I am looking to handle an extensive HTTP API or system, I will reach for the infinitely configurable Faraday. Still, when there are only a few HTTP interactions needed, HTTParty is super simple to use to make HTTP calls.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>Rails</category><category>Gems</category></item><item><title>How to Build a Twitter Clone with Rails, ActionCable and React</title><link>https://robrace.dev/blog/build-a-twitter-clone-with-rails-actioncable-and-react/</link><guid isPermaLink="true">https://robrace.dev/blog/build-a-twitter-clone-with-rails-actioncable-and-react/</guid><description>A response to building a Twitter clone with Rails, CableReady, and StimulusReflex</description><pubDate>Mon, 04 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Let me start by saying I think the things the teams working on CableReady and StimulusReflex are pretty awesome. They are aiming to make working with Reactive web applications as easy as the first Rails Blog tutorials during Rails’ infancy.&lt;/p&gt;
&lt;p&gt;With all of that being said, I am someone who prefers a bit more explicitness and work with tools I already know (and well, a lot more developers in the ecosystem).&lt;/p&gt;
&lt;p&gt;I am not a video guy, don’t have a video set up, and &lt;em&gt;really&lt;/em&gt; prefer not to hear the sound of my own voice. So this will be done through words!&lt;/p&gt;
&lt;p&gt;Without further ado, let’s get started with a new app:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails new blabber --no-spring --webpack=react&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Rails will do its thing, install the application, install the gems, process the Webpacker install, and install the NPM packages needed for React.&lt;/p&gt;
&lt;p&gt;We can jump right into making a model to hold the data to clone what would be a tweet in this Twitter clone. All basic attributes:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails g model Post username body:text likes_count:integer repost_count:integer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;To keep this closely resembling the CableReady/StimulusReflex, we’ll add the same validation in the &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; model:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationRecord&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;validates &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;minimum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;maximum&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;280&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;We’ll make a few small adjustments to the generated migration file to add some database-level defaults (and allows us to keep the code around &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; creation simple):&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CreatePosts&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActiveRecord::Migration&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;6.0&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;change&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;create_table &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Blabby&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;integer&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;t.&lt;/span&gt;&lt;span&gt;timestamps&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Ok! Now, we’re ready to run that migration!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails db:migrate&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the Model and Database layer out of the way, we can move on to the controller and corresponding view templates!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PostsController&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationController&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;@post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(post_params)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ActionCable&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;broadcast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ActionCable&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;broadcast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;find_by&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; params[&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post_id&lt;/span&gt;&lt;span&gt;]).&lt;/span&gt;&lt;span&gt;increment!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;ActionCable&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;broadcast&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;Post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;order&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect_to posts_path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;private&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post_params&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;params.&lt;/span&gt;&lt;span&gt;require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;permit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Simple controller. The &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; action returns a list of posts, to &lt;code dir=&quot;auto&quot;&gt;@post&lt;/code&gt;. &lt;code dir=&quot;auto&quot;&gt;create&lt;/code&gt; uses &lt;code dir=&quot;auto&quot;&gt;StrongParameters&lt;/code&gt;, creates a new Post, broadcasts a message over Websockets (more on that soon), and redirects back to the &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; template. &lt;code dir=&quot;auto&quot;&gt;like&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;repost&lt;/code&gt; are similar except they increment the respective count columns.&lt;/p&gt;
&lt;p&gt;Let’s wire up a few routes to match up to those controller actions. Yes, these aren’t perfect RESTful routes, but 1) They work. 2) This is a 10-minute tutorial. 3) Are GET requests making sure we do not have to worry about AJAX/fetch/CSRF in the front-end. You would obviously work around these issues in a production application.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Rails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;routes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;resources &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;only&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; %i[&lt;/span&gt;&lt;span&gt;index create&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;like&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;get &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;repost&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;posts#index&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With a Model, Controller, and routes, we can put together some view templates. We’ll start by adding the &lt;code dir=&quot;auto&quot;&gt;action_cable_meta_tag&lt;/code&gt; and &lt;code dir=&quot;auto&quot;&gt;Bootstrap&lt;/code&gt; CDN CSS. This way, we can wire up some UI interfaces pretty quick!&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csrf_meta_tags &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; csp_meta_tag &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; stylesheet_link_tag &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;media&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;data-turbolinks-track&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rel&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;stylesheet&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;integrity&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;crossorigin&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;anonymous&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; javascript_pack_tag &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;application&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;data-turbolinks-track&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; action_cable_meta_tag &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yield&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;First up is the &lt;code dir=&quot;auto&quot;&gt;app/views/posts/index.html.erb&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;container&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Blabber&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;A Rails, Actioncable and React demo&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h4&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; render &lt;/span&gt;&lt;span&gt;partial&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;%=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;react_component(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;PostsWrapper&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;, { &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;@posts&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;%&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;&lt;code dir=&quot;auto&quot;&gt;react_component(&lt;/code&gt; is a view helper that is included in &lt;code dir=&quot;auto&quot;&gt;react-rails&lt;/code&gt;, a gem we will install in a minute. Even if you don’t use every feature in the gem, it offers a great way to include a component into an existing view file and the &lt;code dir=&quot;auto&quot;&gt;props&lt;/code&gt; for its first load.&lt;/p&gt;
&lt;p&gt;Up next is a straightforward Rails form:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= form_with model: @post, local: true, html: {class: &apos;my-4&apos; } do |f| %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;form-group&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= f.text_area :body, placeholder: &apos;Enter your blab&apos;, class: &apos;form-control&apos;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rows: 3 %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;actions&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;%= f.submit class: &quot;btn btn-primary&quot; %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;% end %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Alright, that is all we need with ERB files, no we can move onto the &lt;code dir=&quot;auto&quot;&gt;ActionCable&lt;/code&gt; pieces.&lt;/p&gt;
&lt;p&gt;First, we’ll edit the &lt;code dir=&quot;auto&quot;&gt;Connection&lt;/code&gt; file to identify the Cable connection with the browser session ID:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;module&lt;/span&gt;&lt;span&gt; ApplicationCable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Connection&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ActionCable::Connection::Base&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;identified_by &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;session_id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connect&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;session_id&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.&lt;/span&gt;&lt;span&gt;session&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Next, we’ll create a new &lt;code dir=&quot;auto&quot;&gt;Posts&lt;/code&gt; channel:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails g channel PostsChannel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;…and specify the channel we will be using in the stream by a string, &lt;code dir=&quot;auto&quot;&gt;PostsChannel&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt; &amp;#x3C; &lt;/span&gt;&lt;span&gt;ApplicationCable::Channel&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;subscribed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stream_from &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;That does it for the &lt;code dir=&quot;auto&quot;&gt;Actioncable&lt;/code&gt; backend!&lt;/p&gt;
&lt;p&gt;Next up us the &lt;code dir=&quot;auto&quot;&gt;React&lt;/code&gt; side of the application. As we installed &lt;code dir=&quot;auto&quot;&gt;React&lt;/code&gt; with the Rails application command, we can jump to making sure that &lt;code dir=&quot;auto&quot;&gt;react-rails&lt;/code&gt; gem in installed:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gem &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;react-rails&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;gem &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;webpacker&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;~&gt; 4.0&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;With the &lt;code dir=&quot;auto&quot;&gt;Gemfile&lt;/code&gt; update, we can install the gem, use its installer, and add a package to helper connect to &lt;code dir=&quot;auto&quot;&gt;ActionCable&lt;/code&gt; in functional &lt;code dir=&quot;auto&quot;&gt;React&lt;/code&gt; components.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;bundle install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails generate react:install&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;yarn add use-action-cable&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Almost there! We have two React components to add. First up, is a wrapper component that will allow you to wrap your true child component in the &lt;code dir=&quot;auto&quot;&gt;ActionCableProvider&lt;/code&gt;, which provides access to &lt;code dir=&quot;auto&quot;&gt;ActionCable&lt;/code&gt; through a &lt;code dir=&quot;auto&quot;&gt;React Context&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;react&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Posts &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;./Posts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { ActionCableProvider } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;use-action-cable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PostsWrapper&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;ActionCableProvider&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;/cable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;Posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;ActionCableProvider&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Inside the provider, there it passes the &lt;code dir=&quot;auto&quot;&gt;props&lt;/code&gt; to a &lt;code dir=&quot;auto&quot;&gt;Posts&lt;/code&gt; component. The &lt;code dir=&quot;auto&quot;&gt;Post&lt;/code&gt; component:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; React, { useState } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;react&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; PropTypes &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;prop-types&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { useActionCable } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;use-action-cable&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;Posts&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;const [&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setPosts&lt;/span&gt;&lt;span&gt;] = &lt;/span&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;const &lt;/span&gt;&lt;span&gt;channelHandlers&lt;/span&gt;&lt;span&gt; = {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;received&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; =&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;[ActionCable] [Posts]&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;setPosts&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;useActionCable&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;{ channel: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;PostsChannel&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; }, &lt;/span&gt;&lt;span&gt;&lt;span&gt;channelHandlers&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;React.Fragment&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;key&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-body&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-title text-muted&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;float-right&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Posted at &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;created_at&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;small&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;h5&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card-text lead mb-2&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card0link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;/posts/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/repost&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Repost (&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;repost_count&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;card0link&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;/posts/&lt;/span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;/like&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;Likes (&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;likes_count&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;&amp;#x3C;/&lt;/span&gt;&lt;span&gt;React.Fragment&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Posts&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;propTypes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;posts: &lt;/span&gt;&lt;span&gt;PropTypes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;array&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;header_display: &lt;/span&gt;&lt;span&gt;PropTypes&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Posts&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This might be the most complicated file in the whole tutorial! First, we set up some internal state for &lt;code dir=&quot;auto&quot;&gt;Posts&lt;/code&gt;. This allows us to set the incoming &lt;code dir=&quot;auto&quot;&gt;posts&lt;/code&gt; prop as the state, to update when &lt;code dir=&quot;auto&quot;&gt;ActionCable&lt;/code&gt; passes new posts from a broadcast. &lt;code dir=&quot;auto&quot;&gt;channelHandlers&lt;/code&gt; sets up the handler for this &lt;code dir=&quot;auto&quot;&gt;ActionCable&lt;/code&gt; subscription to handler new data. Finally, for &lt;code dir=&quot;auto&quot;&gt;ActionCable&lt;/code&gt; setup, &lt;code dir=&quot;auto&quot;&gt;useActionCable&lt;/code&gt; ties the handler and channel name into a new front-end subscription.&lt;/p&gt;
&lt;p&gt;The return statement returns the JSX template for each post. It is mostly Bootstrap markup but does include two links to reach the earlier created controller actions. As GET requests, they will follow the redirect and reload the &lt;code dir=&quot;auto&quot;&gt;index&lt;/code&gt; template.&lt;/p&gt;
&lt;p&gt;There you go, at this point, it should look like this!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://robrace.dev/.netlify/images?url=_astro%2Fblabber.Dy0y0QuK.gif&amp;#x26;w=577&amp;#x26;h=196&amp;#x26;dpl=6ab305c7c94948000856228f&quot; alt=&quot;Blabber Gif&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;577&quot; height=&quot;196&quot;&gt;&lt;/p&gt;
&lt;p&gt;There you go! I bet with a fast enough system to work through the Rails install, gem install, and javascript package installs, you could make it through this tutorial in less than 10 minutes!&lt;/p&gt;</content:encoded><category>React</category><category>Rails</category></item><item><title>Installing Ruby through RVM</title><link>https://robrace.dev/blog/installing-ruby-through-rvm/</link><guid isPermaLink="true">https://robrace.dev/blog/installing-ruby-through-rvm/</guid><description>Ruby and RVM</description><pubDate>Wed, 29 Apr 2020 19:00:08 GMT</pubDate><content:encoded>&lt;p&gt;I recommend visiting the RMV website (&lt;a href=&quot;https://rvm.io/&quot;&gt;&lt;em&gt;https://rvm.io/&lt;/em&gt;&lt;/a&gt;). Upon the browser rendering the RVM website, you find two simple bash commands you can use to install RVM. I opted not to print the commands in here as 1) a digital security measure and 2) may change as time goes on.&lt;/p&gt;
&lt;p&gt;Once installed, we can begin installing the latest version of Ruby.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;list&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;known&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;This command lists the current versions of Ruby available to install. There may be a lot on the list, but we can focus on the primary interpreter MRI plus, the latest version(2.6.x as of writing). Now, the command to install:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2.6.5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Now sit back or grab a coffee as the install can take anywhere from 1 to many minutes to complete. This wait is based on Ruby package availability, system speed, and plenty of other factors.&lt;/p&gt;
&lt;p&gt;Once the gems are installed, you can now set up a &lt;code dir=&quot;auto&quot;&gt;gemset&lt;/code&gt;. A gemset is a way to group installed gems into a particular RVM based folder. Gemsets keep different projects and their various versions of gems from interfering with each other. Although with Bundler, this isn’t a huge issue, it doesn’t hurt to take the extra step and keep your gems tidy. Additionally, this command instructs RVM to use the recently installed version of Ruby at the same time:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rvm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;use&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2.6.5@sample_app&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;The extra &lt;code dir=&quot;auto&quot;&gt;--create&lt;/code&gt; flag tells RVM to create the gemset if it doesn’t
exist(which it doesn’t).&lt;/p&gt;
&lt;p&gt;Now that we finally have our Ruby and Gemsets in order we can install Rails. We are starting a new sample project to show you the basics of a new Rails app. Later we rerun this command for the SaaS app’s creation. Please refer to the RVM documentation if you want to dig deeper into any other features of RVM past the basics in this chapter.&lt;/p&gt;
</content:encoded><category>Ruby</category></item><item><title>Setting up Rails and PostgreSQL</title><link>https://robrace.dev/blog/setting-up-rails-and-postgresql/</link><guid isPermaLink="true">https://robrace.dev/blog/setting-up-rails-and-postgresql/</guid><description>Rails and PostgreSQL</description><pubDate>Wed, 29 Apr 2020 19:00:08 GMT</pubDate><content:encoded>&lt;p&gt;When building a production-ready application with Rails, the most popular database to use is PostgreSQL. However, Rails via ActiveRecord does support a few databases out of the box. These databases include PostgreSQL, MySQL, and SQLite3. Rails can support other databases through gems and their ORM.&lt;/p&gt;
&lt;p&gt;Since it is most likely you are using both PostgreSQL and MacOS(OSX), I recommend just installing Postgres.app. Simply head to the &lt;a href=&quot;https://postgresapp.com/&quot;&gt;postgres.app&lt;/a&gt; site. Then download and follow the simple instructions to have a PostgreSQL server and library installed on your box.&lt;/p&gt;
&lt;p&gt;Another option you may use if you are skilled in the terminal is to install through &lt;a href=&quot;https://brew.sh&quot;&gt;Brew&lt;/a&gt;. Once you have Brew on your system, you can run &lt;code dir=&quot;auto&quot;&gt;brew install postgresql&lt;/code&gt; and follow the post install prompts.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;rails-new-with-pg&quot;&gt;&lt;code dir=&quot;auto&quot;&gt;rails new&lt;/code&gt; with pg&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;By default, Rails uses SQLite3 for development databases. This database type is used to ease install and development. However, since you are building an app that you intend to deploy, you are going to use PostgreSQL in all of your environments. When initializing a new Rails app, it is quite easy to change the default database type or other default settings.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app_name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-T&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--database=postgresql&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;After a minute or two, Rails will have installed all the required files for the base application. Rails then installed the base gems and ran a few other shell commands to get your application started.&lt;/p&gt;
&lt;div&gt;&lt;h3 id=&quot;database-configuration&quot;&gt;Database Configuration&lt;/h3&gt;&lt;/div&gt;
&lt;p&gt;If everything in the install went correctly, you would be able to use a Rails command to create the database on your local PostgreSQL instance.&lt;/p&gt;
&lt;p&gt;To be safe, take a look at the database configuration file and see what is in there. The database configuration file &lt;code dir=&quot;auto&quot;&gt;config/database.yml&lt;/code&gt; comes with information about installing the &lt;code dir=&quot;auto&quot;&gt;pg&lt;/code&gt; gem. Along with other option settings, you may change if you deviate at any point with your PostgreSQL setup. Here is the relevant development connection information:&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;#x26;&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;adapter&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;postgresql&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;encoding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;unicode&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;pool&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;#x3C;%= ENV.fetch(&quot;RAILS_MAX_THREADS&quot;) { 5 } %&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;development&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;database&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;app_name_development&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Here Rails is setting up a shared configuration between all environments setting the adapter type, encoding, and connection pool settings. None of this is relevant to the default usage of Rails, but you may need to change this at some point in your application’s lifetime.&lt;/p&gt;
&lt;p&gt;The development block specifies to use the default settings and also use a database_name of &lt;code dir=&quot;auto&quot;&gt;app_name_development&lt;/code&gt;. The default naming for Rails databases is &lt;code dir=&quot;auto&quot;&gt;appname_environment&lt;/code&gt;. Meaning your test database name would be &lt;code dir=&quot;auto&quot;&gt;app_name_test&lt;/code&gt; and so on.&lt;/p&gt;
&lt;p&gt;Now, let’s change our directory to that of the Rails app and create the database for Rails to use.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;app_name&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;rails&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;db:create&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
</content:encoded><category>Rails</category><category>PostgreSQL</category></item></channel></rss>