<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Andy Croll]]></title>
  <link href="https://andycroll.com/index.xml" rel="self"/>
  <link href="https://andycroll.com/"/>
  <updated>2026-10-04T12:53:42+00:00</updated>
  <id>https://andycroll.com/</id>
  <author>
    <name><![CDATA[Andy Croll]]></name>
    <email><![CDATA[andy@goodscary.com]]></email>
  </author>
  <generator uri="http://jekyllrb.com/">Jekyll</generator>

  
  <entry>
    <title type="html"><![CDATA[Look at all the things he's not doing]]></title>
    <link href="https://andycroll.com/ruby/look-at-all-the-things-he-is-not-doing/"/>
    <updated>2026-10-02T09:00:00+00:00</updated>
    <id>https://andycroll.com/ruby/look-at-all-the-things-he-is-not-doing</id>
    <content type="html"><![CDATA[<p>There’s a moment in the early seasons of South Park when Cartman et al find the source of an underwear crisis in their mountain town. It’s the <a href="https://en.wikipedia.org/wiki/Gnomes_(South_Park)">underpants gnomes</a> in their cave. They have a plan.</p>

<ol>
  <li>Collect Underpants</li>
  <li>???</li>
  <li>Profit</li>
</ol>

<p>I’ve been considering <a href="https://youtu.be/vDjW_dRyKXY">David’s opening Rails World keynote</a> and realised that this is the main problem I have with it. Rails World is about “shaping the future of Ruby on Rails”. Instead what we got was opinionmaxxing: a lot of certainty about the destination, but <a href="https://youtu.be/vDjW_dRyKXY?t=2761">in his own words</a>, not much that was ‘practical’ or ‘prescriptive’ about getting there.</p>

<ol>
  <li>The model got good at writing code</li>
  <li>???</li>
  <li>Models write all code, we don’t need to care</li>
</ol>

<p>There’s a vibration of uncertainty in our community and the wider industry, even from folks, like me, who were early to playing with and then using the new LLM-powered coding tools and harnesses for real production work.</p>

<p>I was left with a “what’s the point?” feeling. I don’t believe this was the intention. I think David meant it as a provocation, to jolt the room. But the room was already there: when he <a href="https://youtu.be/vDjW_dRyKXY?t=1308">polled the audience</a> only <em>five</em> people out of 1,200 were still writing material amounts of code by hand. He seemed surprised at the scale of that. He shouldn’t have been, the Rails community are tinkerers, early adopters and pragmatists.</p>

<p>To be clear I think he’s directionally right on software. The way we make software has changed forever. Yesterday I shipped (and reviewed) 21 bug fix PRs to CoverageBook, upgraded a Rails app from 7.x and Tailwind 2 to latest versions and shipped a couple of feature PRs. I’m engaging more judgement on how much I code review when I can verify in other ways. I’ve never been so able to deliver well-made software, so fast.</p>

<p>I do believe however there’s a lot of interesting work to do in step 2. Software engineering has always been more than typing code. I’m not seeing evidence I can abdicate my taste to Anthropic, OpenAI or the fast-following open-weight models.</p>

<h2 id="elsewhere">Elsewhere</h2>

<p>There were multiple version numbers mentioned in the keynote: all LLMs, not a single mention of Rails 8.2.</p>

<p>David’s attention is elsewhere. Ruby was <a href="https://youtu.be/vDjW_dRyKXY?t=2205">“about 3%”</a> of his work this year, and he’s <a href="https://youtu.be/vDjW_dRyKXY?t=2892">“talked about nothing else but Omarchy for about 3 months.”</a> Now the models can one-shot a calculator and convert existing applications to Rust. He seems uninterested in the useful guardrails, determinism and token efficiency<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> that a battle-tested framework can provide when using agents.</p>

<p>He did concede, <a href="https://youtu.be/IEOqgPfGLQ8?t=3510">on the panel the next day</a>, that he’d still spin up Rails apps for new web software. And in the keynote he admitted the designers’ vibe-coded work on Basecamp 5 left the architecture <a href="https://youtu.be/vDjW_dRyKXY?t=1370">“a little like Swiss cheese”</a>, and they went back to manual review.</p>

<p>Problem is, the rest of us need to work now, and the next model might not be perfect. The Rails Foundation’s own <a href="https://rubyonrails.org/2026/9/9/agents-on-rails-stage-2">Agents on Rails benchmark</a> says it isn’t there yet: the best result on real feature tickets is 53.3%<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>, and most models score 15–35%. The timeline is unclear. Of course, if the next model removes the need for anything but machine code, this is all moot. But we’re in the aftermath of one tipping point, not past a second, greater one.</p>

<p>Rails’s opinions used to be all about step 2. The keynote put all the certainty on the destination and handed the path to the next model release. That’s the only plan for step 2: <a href="https://youtu.be/vDjW_dRyKXY?t=1395">“give it freaking 5 minutes.”</a> Robby Russell <a href="https://youtu.be/XXjTdyhml0c?t=2845">reflected in his keynote</a> that folks need “somebody willing to say, ‘We’ve learned enough from these experiments. We’re doing it this way now.’”</p>

<h2 id="aim--blast-radius">Aim &amp; Blast Radius</h2>

<p>Jeremy Daer, moderating the <a href="https://youtu.be/IEOqgPfGLQ8">fireside chat between Matz &amp; David</a> <a href="https://youtu.be/IEOqgPfGLQ8?t=1754">asked whether words like “slop” and “meat proxies” were diagnoses or coping mechanisms</a>. David said <a href="https://youtu.be/IEOqgPfGLQ8?t=1785">“90% cope”</a> and that <a href="https://youtu.be/IEOqgPfGLQ8?t=1896">“even the slop grenade is not long for this world.”</a> Jeremy pushed back:</p>

<p><a href="https://youtu.be/IEOqgPfGLQ8?t=1905">“Slop plus the delivery… when it’s being dumped on them”</a>.</p>

<p>That made me reconsider the other elements of that metaphor that make it really sing: the inaccurate trajectory, lobbed and not watched, the implication of a timer and the difference in experience of the thrower and the receiver.</p>

<p>David said <a href="https://youtu.be/IEOqgPfGLQ8?t=1938">“I don’t mind slop grenades at all… I don’t have to catch”</a>. That’s true for him, both in open source (you can refuse a drive-by PR) and at work (he’s in charge).</p>

<p>That’s not what slop grenades end up being for the rest of us though. Most of us can’t ignore PRs from our co-workers or a 3am page from the explosion of a slop-enabled production incident. It’s a thrower’s perspective. We have to catch.</p>

<p>If the models emulate human intelligence with machine determination why wouldn’t having higher level, verifiable, abstractions be useful? That’s why you can spin up a calculator or a Rust duplicate of an existing Rails app. The irresistible force of the agent has something to check its homework on: a bunch of industry standards, a test suite, an existing implementation. Aaron Patterson <a href="https://youtu.be/t0knYnEYBRo?t=3600">made this case</a> from a lower level:</p>

<p>“I’ve heard the argument that AI is just our next compiler… [you don’t read its output] because the compiler made a promise to you… your AI has not made this promise. There is no as-if rule for your English.”</p>

<p>Step 2’s hard part is that the model can’t write your spec. The published failure mode in the foundation’s own AI research is hand-rolling. <a href="https://youtu.be/IEOqgPfGLQ8?t=1515">Matz said it</a>: “we know how to define the goal for the agents… we have to formalize them and teach to the following generations.”</p>

<p>Clarity of aim turns a careless lob into a directed pass. I <a href="https://github.com/andycroll/readability-rb">ported readability</a> from JS to Ruby overnight - its test suite was the guard.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<p>How is David’s Rust-based HEY backend tested? How far along is it? If it’s checked against anything, the best spec is the Rails app it replaces. The six native apps are impressive and I absolutely agree I’d go for a performant API &amp; native app combination when powered by LLMs. But they are all also based on well-established, native, well-maintained, well-documented, platform frameworks like SwiftUI, WinUI, Jetpack Compose, Qt.</p>

<p>The web has one of those too. As <a href="https://youtu.be/QMkZ2J24XGI?t=866">Kinsey Durham Grace</a> put it: “If you designed a framework from scratch for agents, you’d land close to Rails.”</p>

<p>Without aim, a grenade just blows holes in whatever it lands on. Not sure waiting 5 minutes for the models is a good solution to having to solve problems now.</p>

<h2 id="rails-world-answered-its-own-keynote">Rails World answered its own keynote</h2>

<p><a href="https://github.com/basecamp/hotcell">Hot Cell</a> was the only feature that made the keynote<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> but <a href="https://youtu.be/swXl8M84YmM">Mike Dalessio’s talk</a> was absolutely about how AI is affecting open source (and specifically a Rails CVE) and a framework-shaped answer to <em>holy shit the robots are coming to hack your systems</em>.</p>

<p><a href="https://youtu.be/L6-3a_rwLIg">Joël Quenneville</a> &amp; <a href="https://youtu.be/aornGet-EoI">Rachael Wright-Munn</a> talked about fixing the system and bolting deterministic hooks, or <a href="https://youtu.be/aornGet-EoI?t=536">context-injecting generators</a>, onto your agentic tools. Before Rachael’s custom generators carried RubyEvents’ conventions, the AI <a href="https://youtu.be/aornGet-EoI?t=116">“would invent keys”</a>. Merging <a href="https://youtu.be/REtfdqtW3PY">Marco Roth’s Herb</a> into Rails gives us a parsing tool for the view layer that can escalate errors into an agent’s context.</p>

<p><a href="https://youtu.be/QMkZ2J24XGI">Kinsey</a> also brought numbers from her time on GitHub’s Copilot coding agent team: 84% of developers use AI to write code, but only 29% trust it. <a href="https://youtu.be/QMkZ2J24XGI?t=164">“Generation is really, really cheap, and verification is not”</a>, so reviewing harder won’t fix it. Her answer: <a href="https://youtu.be/QMkZ2J24XGI?t=1238">“Constraints beat instruction”</a>. A rule in AGENTS.md fades as the context fills; a CI check that fails the build applies every time and costs no tokens. It’s also why Rachael’s generators work. With a generator, <a href="https://youtu.be/QMkZ2J24XGI?t=1573">“you’re making the command that puts [service objects] there cheaper than writing the file by hand. It’ll take the cheap path because agents always do.”</a></p>

<p>Robby argued for improving the “workshop” of our apps, making the preferred way obvious for humans and agents. Applying our curiosity to the problems we see in our day to day and leaving things better than we found them.</p>

<p>Jeremy <a href="https://youtu.be/IEOqgPfGLQ8?t=3131">asked what’s past Rails</a>: “I want to keep Rails… what about the values of Rails?” The answer was <a href="https://youtu.be/IEOqgPfGLQ8?t=3250">“find your tribe… stick around.”</a> That’s not a plan for Rails. David’s own tribe, and his energy, is clearly Omarchy, his agent-focussed Linux distribution.</p>

<p>People move on. That’s fine. There’s an entire army of people wanting to do, and already doing, the work before the models (maybe) do all software engineering and the framework doesn’t matter.</p>

<p>8.1.4, <a href="https://rubyonrails.org/2026/9/24/Rails-Version-8-1-4-has-been-released">a bugfix release</a>, came out on day 2 of the conference. There’s a bunch of cool stuff in 8.2. There’s no doubt an editing job to do to make Rails useful through the AI-chaos era that <a href="https://youtu.be/swXl8M84YmM?t=1527">Mike identified in his talk</a>.</p>

<h2 id="look-at-all-the-things-we-can-do">Look at all the things we can do</h2>

<p>There’s an alt-history version of this talk that pivots on the delight of David’s 21-year-old demo <a href="https://youtu.be/vDjW_dRyKXY?t=1144">“look at all the things I’m not doing”</a> into a vision for how Rails helps the models and the humans compress and accelerate. Then it was the framework, now it’s the framework and the model. David credited only the model.</p>

<p>Kinsey closed with the version I wanted from the keynote: <a href="https://youtu.be/QMkZ2J24XGI?t=1795">“Convention over configuration was never really about productivity. It was about care. A predictable and clear place to put things is a message to whoever shows up next… We called it a framework, because somebody always comes next.”</a></p>

<p>No doubt Rails needs to change—some things in, some things out—but it felt to me like a resignation from this version of his world view. If David’s focus has moved on, that’s his call. There was enough can-do, roll-your-sleeves-up attitude in the rest of the conference to take on the uncertain future.</p>

<p>Mike <a href="https://youtu.be/swXl8M84YmM?t=62">opened with a timeline</a>: prehistory to modern computing, then “you are here” in a band marked “CHAOS!”, and a giant question mark. That question mark <em>is</em> step 2. It won’t look like the old work. But it’s our work, and you don’t need permission to do it.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>He <a href="https://youtu.be/vDjW_dRyKXY?t=2038">praised it in the keynote</a> and called it <a href="https://youtu.be/IEOqgPfGLQ8?t=2736">‘a short-term consideration’</a> the next day. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;&#xFE0E;</a></p>
    </li>
    <li id="fn:2">
      <p>Stage 2 gives models 20 feature tickets, written the way a product person would, against 37signals’ Fizzy app, graded by hidden checks. At default settings the <a href="https://rubyonrails.org/2026/9/9/agents-on-rails-stage-2">best score was 35%</a>. The 53.3% is the same model, GPT-6 Astra, at <a href="https://rubyonrails.org/2026/9/21/agents-on-rails-maximum-effort-and-deepseek-4-1-flash">maximum effort</a>: 2.6 times the cost and almost three times the time. Claude Fable 5.1 spent twice as much at max for the same 32%. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;&#xFE0E;</a></p>
    </li>
    <li id="fn:3">
      <p>Simon Willison <a href="https://simonwillison.net/2025/Dec/15/porting-justhtml/">ported</a> a standards-compliant HTML parser (<a href="https://github.com/EmilStenstrom/justhtml">JustHTML</a>) from Python to JS in four and a half hours. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;&#xFE0E;</a></p>
    </li>
    <li id="fn:4">
      <p>In passing, at <a href="https://youtu.be/vDjW_dRyKXY?t=3585">59:45</a>. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;&#xFE0E;</a></p>
    </li>
  </ol>
</div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Use class_names to Conditionally Apply CSS Classes]]></title>
    <link href="https://andycroll.com/ruby/use-class-names-to-conditionally-apply-css-classes/"/>
    <updated>2026-05-11T09:00:00+00:00</updated>
    <id>https://andycroll.com/ruby/use-class-names-to-conditionally-apply-css-classes</id>
    <content type="html"><![CDATA[<p>When you’re building views in Rails, you often need to apply CSS classes conditionally. Maybe a nav link should look different when it’s the current page, or a form field needs error styling. Since Rails 6.1, the <a href="https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-class_names"><code class="language-plaintext highlighter-rouge">class_names</code></a> helper does this cleanly.</p>

<h2 id="instead-of">Instead of…</h2>

<p>…interpolating conditional classes with ternaries or post-statement conditionals:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"p-4 rounded </span><span class="cp">&lt;%=</span> <span class="vi">@error</span> <span class="p">?</span> <span class="s1">'bg-red-50 border-red-500'</span> <span class="p">:</span> <span class="s1">''</span> <span class="cp">%&gt;</span><span class="s"> </span><span class="cp">&lt;%=</span> <span class="s1">'opacity-50 cursor-not-allowed'</span> <span class="k">if</span> <span class="vi">@disabled</span> <span class="cp">%&gt;</span><span class="s">"</span><span class="nt">&gt;</span>
  <span class="cp">&lt;%=</span> <span class="vi">@message</span> <span class="cp">%&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<h2 id="use">Use…</h2>

<p>…the <code class="language-plaintext highlighter-rouge">class_names</code> helper:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">tag</span><span class="p">.</span><span class="nf">div</span> <span class="ss">class: </span><span class="n">class_names</span><span class="p">(</span>
  <span class="s2">"p-4 rounded"</span><span class="p">,</span>
  <span class="s2">"bg-red-50 border-red-500"</span><span class="p">:</span> <span class="vi">@error</span><span class="p">,</span>
  <span class="s2">"opacity-50 cursor-not-allowed"</span><span class="p">:</span> <span class="vi">@disabled</span>
<span class="p">)</span> <span class="k">do</span> <span class="cp">%&gt;</span>
  <span class="cp">&lt;%=</span> <span class="vi">@message</span> <span class="cp">%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">String</code> arguments are always applied; trailing keyword-style entries are included when their value is truthy and silently dropped otherwise.</p>

<p>Better still — Rails tag helpers (<code class="language-plaintext highlighter-rouge">tag.*</code>, <code class="language-plaintext highlighter-rouge">link_to</code>, form builders) already run the <code class="language-plaintext highlighter-rouge">class:</code> argument through the process implicitly, so you can drop the wrapper and hand it an array directly. The trailing <code class="language-plaintext highlighter-rouge">key: value</code> pairs don’t need their own <code class="language-plaintext highlighter-rouge">{}</code> either; Ruby wraps them into a <code class="language-plaintext highlighter-rouge">Hash</code> for you:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">tag</span><span class="p">.</span><span class="nf">div</span> <span class="ss">class: </span><span class="p">[</span>
  <span class="s2">"p-4 rounded"</span><span class="p">,</span>
  <span class="s2">"bg-red-50 border-red-500"</span><span class="p">:</span> <span class="vi">@error</span><span class="p">,</span>
  <span class="s2">"opacity-50 cursor-not-allowed"</span><span class="p">:</span> <span class="vi">@disabled</span>
<span class="p">]</span> <span class="k">do</span> <span class="cp">%&gt;</span>
  <span class="cp">&lt;%=</span> <span class="vi">@message</span> <span class="cp">%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>An active nav link:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Home"</span><span class="p">,</span> <span class="n">root_path</span><span class="p">,</span>
  <span class="ss">class: </span><span class="p">[</span><span class="s2">"nav-link px-3 py-2"</span><span class="p">,</span>
    <span class="s2">"text-blue-700 font-semibold"</span><span class="p">:</span> <span class="n">current_page?</span><span class="p">(</span><span class="n">root_path</span><span class="p">)]</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>A form field with errors:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">f</span><span class="p">.</span><span class="nf">text_field</span> <span class="ss">:email</span><span class="p">,</span>
  <span class="ss">class: </span><span class="p">[</span><span class="s2">"field"</span><span class="p">,</span> <span class="s2">"field--error"</span><span class="p">:</span> <span class="vi">@user</span><span class="p">.</span><span class="nf">errors</span><span class="p">[</span><span class="ss">:email</span><span class="p">].</span><span class="nf">any?</span><span class="p">]</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>A flash message:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%</span> <span class="n">flash</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">type</span><span class="p">,</span> <span class="n">message</span><span class="o">|</span> <span class="cp">%&gt;</span>
  <span class="cp">&lt;%=</span> <span class="n">tag</span><span class="p">.</span><span class="nf">p</span> <span class="n">message</span><span class="p">,</span> <span class="ss">class: </span><span class="p">[</span><span class="s2">"flash"</span><span class="p">,</span>
    <span class="ss">notice: </span><span class="n">type</span> <span class="o">==</span> <span class="s2">"notice"</span><span class="p">,</span>
    <span class="ss">alert: </span><span class="n">type</span> <span class="o">==</span> <span class="s2">"alert"</span><span class="p">]</span> <span class="cp">%&gt;</span>
<span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>An active tab:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s2">"Overview"</span><span class="p">,</span> <span class="n">project_path</span><span class="p">(</span><span class="vi">@project</span><span class="p">),</span>
  <span class="ss">class: </span><span class="p">[</span><span class="s2">"tab"</span><span class="p">,</span> <span class="s2">"tab--active"</span><span class="p">:</span> <span class="n">current_page?</span><span class="p">(</span><span class="n">project_path</span><span class="p">(</span><span class="vi">@project</span><span class="p">))]</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<p>Or wrap a repeated pattern in a helper. Helpers often return <code class="language-plaintext highlighter-rouge">String</code>s, which would be a case where you call <code class="language-plaintext highlighter-rouge">class_names</code> directly:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">class_names_for_project</span><span class="p">(</span><span class="n">project</span><span class="p">)</span>
  <span class="n">class_names</span><span class="p">(</span><span class="s2">"status-badge"</span><span class="p">,</span>
    <span class="s2">"status-badge--primary"</span><span class="p">:</span> <span class="n">project</span><span class="p">.</span><span class="nf">active?</span><span class="p">,</span>
    <span class="s2">"status-badge--muted"</span><span class="p">:</span> <span class="n">project</span><span class="p">.</span><span class="nf">archived?</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">tag</span><span class="p">.</span><span class="nf">span</span> <span class="vi">@project</span><span class="p">.</span><span class="nf">status</span><span class="p">,</span> <span class="ss">class: </span><span class="n">class_names_for_project</span><span class="p">(</span><span class="vi">@project</span><span class="p">)</span> <span class="cp">%&gt;</span>
</code></pre></div></div>

<h2 id="why">Why?</h2>

<p>The unsophisticated approach ends up with extra whitespace in the rendered HTML with ERB tags inside an HTML attribute (which I’m not a fan of visually), plus it’s hard to scan which classes are always present and which are conditional.</p>

<p>The tag helpers call <code class="language-plaintext highlighter-rouge">token_list</code> on whatever you pass to <code class="language-plaintext highlighter-rouge">class:</code>, which is aliased as <code class="language-plaintext highlighter-rouge">class_names</code>. It splits whitespace-separated tokens, deduplicates them, and returns an HTML-safe string. So <code class="language-plaintext highlighter-rouge">["p-4", "p-4 rounded"]</code> collapses to <code class="language-plaintext highlighter-rouge">"p-4 rounded"</code> rather than repeating <code class="language-plaintext highlighter-rouge">p-4</code>.</p>

<p>You have to call <code class="language-plaintext highlighter-rouge">class_names</code> directly when you’re not inside a tag helper — building a string in a helper method, or interpolating into raw HTML. It’s available in all views and helpers in Rails since it’s defined in <code class="language-plaintext highlighter-rouge">ActionView::Helpers::TagHelper</code>. You might also see it referred to as <code class="language-plaintext highlighter-rouge">token_list</code>, which is the original method name.</p>

<h2 id="why-not">Why not?</h2>

<p>If you’ve only got a single conditional class, plain ERB is readable enough:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"p-4 </span><span class="cp">&lt;%=</span> <span class="s1">'font-bold'</span> <span class="k">if</span> <span class="vi">@important</span> <span class="cp">%&gt;</span><span class="s">"</span><span class="nt">&gt;</span>
</code></pre></div></div>

<p>Though the array form does avoid the awkward whitespace issue when the condition is false:</p>

<div class="language-erb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;%=</span> <span class="n">tag</span><span class="p">.</span><span class="nf">div</span> <span class="ss">class: </span><span class="p">[</span><span class="s2">"p-4"</span><span class="p">,</span> <span class="s2">"font-bold"</span><span class="p">:</span> <span class="vi">@important</span><span class="p">]</span> <span class="k">do</span> <span class="cp">%&gt;</span>
</code></pre></div></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Avoid html_safe with Tag Helpers, safe_join, and sanitize]]></title>
    <link href="https://andycroll.com/ruby/alternatives-to-html-safe-in-rails/"/>
    <updated>2026-05-04T09:00:00+00:00</updated>
    <id>https://andycroll.com/ruby/alternatives-to-html-safe-in-rails</id>
    <content type="html"><![CDATA[<p>When you need to build HTML outside of a template, it’s tempting to concatenate strings and call <code class="language-plaintext highlighter-rouge">html_safe</code> on the result. This bypasses Rails’s built-in <a href="/ruby/beware-of-raw-erb/">XSS protection</a> entirely: any user input in that string goes straight to the browser unescaped.</p>

<p>The good news is you almost never need <code class="language-plaintext highlighter-rouge">html_safe</code>. Rails provides three underappreciated tools that handle escaping for you.</p>

<h2 id="instead-of">Instead of…</h2>

<p>…calling <code class="language-plaintext highlighter-rouge">html_safe</code> on strings you’ve built by hand:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">status_badge</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="n">color</span><span class="p">)</span>
  <span class="s2">"&lt;span class=</span><span class="se">\"</span><span class="s2">badge badge-</span><span class="si">#{</span><span class="n">color</span><span class="si">}</span><span class="se">\"</span><span class="s2">&gt;</span><span class="si">#{</span><span class="n">label</span><span class="si">}</span><span class="s2">&lt;/span&gt;"</span><span class="p">.</span><span class="nf">html_safe</span>
<span class="k">end</span>
</code></pre></div></div>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">formatted_address</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
  <span class="p">[</span><span class="n">user</span><span class="p">.</span><span class="nf">street</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="nf">city</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="nf">postcode</span><span class="p">].</span><span class="nf">compact</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s2">"&lt;br&gt;"</span><span class="p">).</span><span class="nf">html_safe</span>
<span class="k">end</span>
</code></pre></div></div>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">render_comment</span><span class="p">(</span><span class="n">comment</span><span class="p">)</span>
  <span class="n">comment</span><span class="p">.</span><span class="nf">body_html</span><span class="p">.</span><span class="nf">html_safe</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="use">Use…</h2>

<p>…the right tool for each situation.</p>

<p>When you need to <strong>build HTML elements</strong>, use <code class="language-plaintext highlighter-rouge">tag</code> helpers:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">status_badge</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="n">color</span><span class="p">)</span>
  <span class="n">tag</span><span class="p">.</span><span class="nf">span</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="ss">class: </span><span class="s2">"badge badge-</span><span class="si">#{</span><span class="n">color</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<p>The <a href="https://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html"><code class="language-plaintext highlighter-rouge">tag</code> helper</a> escapes the content and attributes automatically. It returns an HTML-safe string without you having to think about it.</p>

<p>When you need to <strong>join fragments</strong> that mix safe HTML with potentially unsafe text, use <code class="language-plaintext highlighter-rouge">safe_join</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">formatted_address</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
  <span class="n">safe_join</span><span class="p">([</span><span class="n">user</span><span class="p">.</span><span class="nf">street</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="nf">city</span><span class="p">,</span> <span class="n">user</span><span class="p">.</span><span class="nf">postcode</span><span class="p">].</span><span class="nf">compact</span><span class="p">,</span> <span class="n">tag</span><span class="p">.</span><span class="nf">br</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<p><a href="https://api.rubyonrails.org/classes/ActionView/Helpers/OutputSafetyHelper.html#method-i-safe_join"><code class="language-plaintext highlighter-rouge">safe_join</code></a> escapes any unsafe strings in the array and returns an HTML-safe result. It’s <code class="language-plaintext highlighter-rouge">Array#join</code> with protection built in.</p>

<p>When you need to <strong>accept user-provided HTML</strong> but strip dangerous tags, use <code class="language-plaintext highlighter-rouge">sanitize</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">render_comment</span><span class="p">(</span><span class="n">comment</span><span class="p">)</span>
  <span class="n">sanitize</span><span class="p">(</span><span class="n">comment</span><span class="p">.</span><span class="nf">body_html</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>

<p><a href="https://api.rubyonrails.org/classes/ActionView/Helpers/SanitizeHelper.html#method-i-sanitize"><code class="language-plaintext highlighter-rouge">sanitize</code></a> keeps safe tags like <code class="language-plaintext highlighter-rouge">&lt;p&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;strong&gt;</code>, and <code class="language-plaintext highlighter-rouge">&lt;em&gt;</code> while stripping <code class="language-plaintext highlighter-rouge">&lt;script&gt;</code>, event handlers, and other XSS vectors. You can customise the allowed tags and attributes, but beware of straying past the defaults — they are battle-tested and loosening them is a <a href="https://en.wiktionary.org/wiki/footgun">footgun</a>.</p>

<h2 id="why">Why?</h2>

<p>Each of these tools lets Rails manage HTML safety for you. You describe what you want — an element, a joined list, sanitised content — and the framework handles the escaping.</p>

<p><code class="language-plaintext highlighter-rouge">html_safe</code> does the opposite. It tells Rails “trust this string, don’t escape it”. That’s a promise <em>you</em> have to keep, and it’s easy to break when the inputs change or a future developer doesn’t realise user data flows through that path. Ask your friendly security consultant or penetration testing organisation why this is a bad idea.</p>

<p>The mental model is simple. Need an HTML element? <code class="language-plaintext highlighter-rouge">tag</code>. Joining fragments? <code class="language-plaintext highlighter-rouge">safe_join</code>. Accepting rich text? <code class="language-plaintext highlighter-rouge">sanitize</code>. If none of those fit, you probably need a partial or a component, not a string.</p>

<h2 id="why-not">Why not?</h2>

<p>There are legitimate uses of <code class="language-plaintext highlighter-rouge">html_safe</code>. Some gems, like <a href="https://github.com/ddnexus/pagy"><code class="language-plaintext highlighter-rouge">pagy</code></a>, return pre-built HTML strings that are safe by construction. Calling <code class="language-plaintext highlighter-rouge">html_safe</code> on their output is fine because the gem controls the content.</p>

<p>You might also see <code class="language-plaintext highlighter-rouge">html_safe</code> on strings that are genuinely static with no user input, like <code class="language-plaintext highlighter-rouge">"&amp;nbsp;".html_safe</code>. That’s harmless, but you can include the actual Unicode character instead — <code class="language-plaintext highlighter-rouge">"\u00A0"</code> gives you a non-breaking space without needing <code class="language-plaintext highlighter-rouge">html_safe</code> at all. That is ugly as hell though, so it’s your call!</p>

<p>The key question is always: could user input end up in this string? If the answer is yes, or even <em>maybe</em>, reach for <code class="language-plaintext highlighter-rouge">tag</code>, <code class="language-plaintext highlighter-rouge">safe_join</code>, or <code class="language-plaintext highlighter-rouge">sanitize</code> instead.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Use Rails Combined Credentials]]></title>
    <link href="https://andycroll.com/ruby/use-rails-combined-credentials/"/>
    <updated>2026-04-13T06:00:00+00:00</updated>
    <id>https://andycroll.com/ruby/use-rails-combined-credentials</id>
    <content type="html"><![CDATA[<p>To deal with secrets and credential handling most Rails apps have ended up with a hotchpotch of <code class="language-plaintext highlighter-rouge">ENV.fetch</code> calls and <code class="language-plaintext highlighter-rouge">credentials.dig</code> lookups throughout the codebase, depending on where each secret lives.</p>

<p>Rails edge — and the upcoming 8.2 — fixes this.</p>

<h2 id="instead-of">Instead of…</h2>

<p>…mixing ENV and credential lookups:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">StripeChargeService</span>
  <span class="k">def</span> <span class="nf">initialize</span>
    <span class="vi">@api_key</span> <span class="o">=</span> <span class="no">ENV</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"STRIPE_API_KEY"</span><span class="p">)</span>
    <span class="vi">@webhook_secret</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">credentials</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="ss">:stripe</span><span class="p">,</span> <span class="ss">:webhook_secret</span><span class="p">)</span>
    <span class="vi">@price_id</span> <span class="o">=</span> <span class="no">ENV</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="s2">"STRIPE_PRICE_ID"</span><span class="p">)</span> <span class="p">{</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">credentials</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="ss">:stripe</span><span class="p">,</span> <span class="ss">:price_id</span><span class="p">)</span> <span class="p">}</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<h2 id="use">Use…</h2>

<p>…the combined credentials API:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">StripeChargeService</span>
  <span class="k">def</span> <span class="nf">initialize</span>
    <span class="vi">@api_key</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">app</span><span class="p">.</span><span class="nf">creds</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:stripe</span><span class="p">,</span> <span class="ss">:api_key</span><span class="p">)</span>
    <span class="vi">@webhook_secret</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">app</span><span class="p">.</span><span class="nf">creds</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:stripe</span><span class="p">,</span> <span class="ss">:webhook_secret</span><span class="p">)</span>
    <span class="vi">@price_id</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">app</span><span class="p">.</span><span class="nf">creds</span><span class="p">.</span><span class="nf">require</span><span class="p">(</span><span class="ss">:stripe</span><span class="p">,</span> <span class="ss">:price_id</span><span class="p">)</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">require</code> raises a <code class="language-plaintext highlighter-rouge">KeyError</code> if the key is missing from all backends. For optional values, use <code class="language-plaintext highlighter-rouge">option</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Rails</span><span class="p">.</span><span class="nf">app</span><span class="p">.</span><span class="nf">creds</span><span class="p">.</span><span class="nf">option</span><span class="p">(</span><span class="ss">:appsignal</span><span class="p">,</span> <span class="ss">:push_api_key</span><span class="p">,</span> <span class="ss">default: </span><span class="kp">nil</span><span class="p">)</span>
<span class="c1"># Returns nil if missing — AppSignal just won't report</span>
</code></pre></div></div>

<p>To keep production secrets separate, run <code class="language-plaintext highlighter-rouge">bin/rails credentials:edit --environment production</code>. This creates a separate encrypted file with its own key:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>config/credentials.yml.enc         ← shared (dev/test)
config/master.key                  ← decrypts the shared file

config/credentials/production.yml.enc  ← production only
config/credentials/production.key      ← decrypts production
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">production.yml.enc</code> exists, Rails uses it exclusively in production — there’s no inheritance from the shared file, so duplicate any keys you need. To decrypt in production, set <code class="language-plaintext highlighter-rouge">RAILS_MASTER_KEY</code> in your hosting provider to the contents of <code class="language-plaintext highlighter-rouge">production.key</code>.</p>

<h2 id="why">Why?</h2>

<p><code class="language-plaintext highlighter-rouge">Rails.app.creds</code> checks ENV first, then falls back to encrypted credentials. You don’t need to know or care where a value is stored.</p>

<p>Nested keys like <code class="language-plaintext highlighter-rouge">:stripe, :api_key</code> map to double-underscored ENV names (<code class="language-plaintext highlighter-rouge">STRIPE__API_KEY</code>). A single key like <code class="language-plaintext highlighter-rouge">:postmark_api_token</code> checks <code class="language-plaintext highlighter-rouge">ENV["POSTMARK_API_TOKEN"]</code>.</p>

<p>This means you can move secrets between ENV and encrypted credentials without changing application code. Deploying to a provider that injects secrets via ENV? It just works. Want to move a key into the encrypted file instead? Remove the ENV variable and add it to your credentials. Your code stays the same.</p>

<p>I’ve <a href="/ruby/wrap-your-environment-variables-in-a-settings-object/">previously recommended</a> wrapping ENV in a custom Settings object. This built-in approach is better — the same clean interface with the added fallback to encrypted credentials.</p>

<h2 id="why-not">Why not?</h2>

<p>This isn’t in a released version of Rails yet — you need Rails edge (<code class="language-plaintext highlighter-rouge">main</code>), and it’s expected in Rails 8.2. If you’re on 8.1 or older, a <a href="/ruby/wrap-your-environment-variables-in-a-settings-object/">custom Settings wrapper</a> still works well.</p>

<h3 id="other-considerations">Other Considerations</h3>

<p>You can also create <code class="language-plaintext highlighter-rouge">development.yml.enc</code> and <code class="language-plaintext highlighter-rouge">test.yml.enc</code>, but I think the shared file plus a production override is clearer — and you shouldn’t be calling real APIs in your test environment anyhow.</p>

<p>Keep separate encryption keys for each environment. You could share one, but a leaked development key shouldn’t expose production secrets.</p>

<h2 id="mea-culpa">Mea Culpa</h2>

<p>I originally published this post saying <code class="language-plaintext highlighter-rouge">Rails.app.creds</code> had shipped in Rails 8.1. It hasn’t — it’s on Rails <code class="language-plaintext highlighter-rouge">main</code> and is expected in 8.2. I’ve been running Rails edge on a couple of projects and assumed this had already been released. Apologies for the confusion, and thanks to everyone who pointed it out.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Teach Rails Irregular Plurals with Inflections]]></title>
    <link href="https://andycroll.com/ruby/teach-rails-irregular-plurals-with-inflections/"/>
    <updated>2026-03-30T06:00:00+00:00</updated>
    <id>https://andycroll.com/ruby/teach-rails-irregular-plurals-with-inflections</id>
    <content type="html"><![CDATA[<p>English has plenty of irregular plurals. Criterion becomes criteria, not criterions. Rails handles many common ones already, but your domain might include words it doesn’t know about.</p>

<h2 id="instead-of">Instead of…</h2>

<p>…accepting Rails’s best guess at a plural:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"criterion"</span><span class="p">.</span><span class="nf">pluralize</span>  <span class="c1">#=&gt; "criterions"</span>
<span class="s2">"matrix"</span><span class="p">.</span><span class="nf">pluralize</span>     <span class="c1">#=&gt; "matrices"  # this one Rails knows!</span>
</code></pre></div></div>

<h2 id="use">Use…</h2>

<p>…<code class="language-plaintext highlighter-rouge">inflect.irregular</code> to teach Rails the correct pair:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># config/initializers/inflections.rb</span>
<span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Inflector</span><span class="p">.</span><span class="nf">inflections</span><span class="p">(</span><span class="ss">:en</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">inflect</span><span class="o">|</span>
  <span class="n">inflect</span><span class="p">.</span><span class="nf">irregular</span> <span class="s2">"criterion"</span><span class="p">,</span> <span class="s2">"criteria"</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Now:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"criterion"</span><span class="p">.</span><span class="nf">pluralize</span>  <span class="c1">#=&gt; "criteria"</span>
<span class="s2">"criteria"</span><span class="p">.</span><span class="nf">singularize</span> <span class="c1">#=&gt; "criterion"</span>
</code></pre></div></div>

<h2 id="why">Why?</h2>

<p>Give <code class="language-plaintext highlighter-rouge">irregular</code> the singular and plural forms and Rails handles both directions—<code class="language-plaintext highlighter-rouge">pluralize</code> and <code class="language-plaintext highlighter-rouge">singularize</code> both work correctly.</p>

<p>A <code class="language-plaintext highlighter-rouge">Criterion</code> model will look for a <code class="language-plaintext highlighter-rouge">criteria</code> table. <code class="language-plaintext highlighter-rouge">resources :criteria</code> will route to <code class="language-plaintext highlighter-rouge">CriteriaController</code>. Association names, fixtures, and factory names all follow suit.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Criterion</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
  <span class="c1"># table: criteria</span>
<span class="k">end</span>

<span class="k">class</span> <span class="nc">Survey</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
  <span class="n">has_many</span> <span class="ss">:criteria</span>  <span class="c1"># works as expected</span>
<span class="k">end</span>
</code></pre></div></div>

<p>You can declare as many as you need:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Inflector</span><span class="p">.</span><span class="nf">inflections</span><span class="p">(</span><span class="ss">:en</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">inflect</span><span class="o">|</span>
  <span class="n">inflect</span><span class="p">.</span><span class="nf">irregular</span> <span class="s2">"criterion"</span><span class="p">,</span> <span class="s2">"criteria"</span>
  <span class="n">inflect</span><span class="p">.</span><span class="nf">irregular</span> <span class="s2">"goose"</span><span class="p">,</span> <span class="s2">"geese"</span>
<span class="k">end</span>
</code></pre></div></div>

<p>Although, unless you’re building some kind of flighted animal tracker, you probably won’t need that second one.</p>

<p>Rails already knows a handful of irregular plurals: person/people, man/men, child/children, sex/sexes, move/moves, and—crucially—zombie/zombies are built in. Rails’s pluralisation rules are regex-based, so the <code class="language-plaintext highlighter-rouge">(m)an → (m)en</code> pattern also covers woman/women. But that’s it—words like tooth/teeth, foot/feet, mouse/mice, and goose/geese are not handled by default. Check the <a href="https://github.com/rails/rails/blob/main/activesupport/lib/active_support/inflections.rb">default inflections</a> to see what’s already covered.</p>

<p>See the <a href="https://api.rubyonrails.org/classes/ActiveSupport/Inflector/Inflections.html#method-i-irregular">ActiveSupport::Inflector::Inflections documentation</a> for details.</p>

<h2 id="why-not">Why not?</h2>

<p>Before adding an irregular inflection, check whether Rails already knows the word. Try it in a console:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"person"</span><span class="p">.</span><span class="nf">pluralize</span>  <span class="c1">#=&gt; "people"  — already works</span>
<span class="s2">"axis"</span><span class="p">.</span><span class="nf">pluralize</span>    <span class="c1">#=&gt; "axes"    — already works</span>
</code></pre></div></div>

<p>If it’s already correct, adding it to your initialiser is just noise.</p>

<p>If the word never appears as a model or resource name, there’s no reason to declare it.</p>

<p>For words that don’t change between singular and plural (like “sheep” or “metadata”), you need <code class="language-plaintext highlighter-rouge">inflect.uncountable</code>. For casing issues with acronyms like API or CSV, look at <code class="language-plaintext highlighter-rouge">inflect.acronym</code>.</p>
]]></content>
  </entry>
  
</feed>
