Skip to content

Ruby on Rails

Next release

How midcode starts a Rails app, edits the HTML written in its ERB views on the canvas, reads its routes as pages, and what helpers like link_to leave out of reach.

View as Markdown

This ships with the next release of midcode. The version you can download today (1.1.2) doesn’t have it yet.

midcode opens a Rails app, starts its server, and edits the HTML written in its ERB views, layouts and partials: text, classes, attributes, new elements and their order. What ERB works out (<%= post.title %>) and the tags that helpers build (link_to, image_tag, form_with) aren’t editable on the canvas.

Rails renders the page, so midcode finds each element in your views after the page loads. Sites a server renders explains how that works and what it means for editing.

At a glance

Ruby on Rails
Detected bybin/rails or config/application.rb
Runs withbin/rails server -b 127.0.0.1 -p $PORT
Elements are marked bymidcode, after each page loads, by matching it against the app’s .erb files
EditingText, classes, attributes and tag; insert, move, duplicate and remove
ComponentsA partial is edited in its own file
Pagesconfig/routes.rb: root, get and resources
StylesTailwind 4 classes, or mid: utilities with public/midcode.css
Tried withRails 8: 15 of 16 elements found on a page with a layout, a view, a partial in a loop and an if / else

How midcode runs it

When the folder has bin/rails or config/application.rb, midcode runs this in your login shell, in the project’s folder:

Terminal
bin/rails server -b 127.0.0.1 -p $PORT

$PORT is a free port midcode picks. The command finds Ruby the way your terminal does, so a version manager set up in your shell applies. What Rails prints is in the Dev server log in the top bar. Nothing in the app is changed to run it.

You need Ruby, the app’s gems installed (bundle install) and its database prepared. midcode doesn’t run either on a project it opens.

midcode runs the server, not bin/dev. If your app counts on Procfile.dev to build its CSS or JavaScript, run those watchers in a terminal, or set the command yourself with Change how it runs (below).

What you can edit

Anything written as HTML in an .erb file. Code between <% and %> is never touched.

app/views/posts/index.html.erb
<section class="posts">
  <h1>Latest posts</h1>
  <% @posts.each do |post| %>
    <article class="post <%= 'featured' if post.featured? %>">
      <h2><%= post.title %></h2>
      <%= link_to "Read more", post, class: "more" %>
    </article>
  <% end %>
</section>
  • “Latest posts” is written here: double-click it on the canvas.

  • The <article> is one line shown once per post. The panel changes post and what you add next to it, and leaves the ERB inside the attribute alone. A change applies to every post.

  • The <h2> is found. Its text is post.title, which isn’t in the file.

  • The link has no mark: link_to builds the <a>, and no file writes that tag. On the page that was tried, the one element of 16 that wasn’t found was a link_to.

To make that link editable, write the tag yourself. Its class and its words can then be changed on the canvas, and the address stays computed:

app/views/posts/index.html.erb
-      <%= link_to "Read more", post, class: "more" %>+      <a class="more" href="<%= post_path(post) %>">Read more</a>

A partial rendered in a loop (_post.html.erb) works like the <article> above: every copy on the page points at the same line of the partial.

Pages that Turbo swaps in without a full load are matched again as they arrive.

After an edit midcode reloads the page in every breakpoint.

Pages

Pages in the top bar is filled from config/routes.rb, read as text:

In routes.rbPageView it opens
root "pages#home"Homeapp/views/pages/home.html.erb
get "about", to: "pages#about"/aboutapp/views/pages/about.html.erb
get "posts/:id", to: "posts#show"/posts/[id]app/views/posts/show.html.erb
resources :posts/posts and /posts/[id]app/views/posts/index.html.erb and show.html.erb

only: on a resources line is respected. A dynamic page finds its real addresses from the running site’s sitemap and links (Pages and navigation). For any page that isn’t listed, type its path in Pages and press Enter.

midcode doesn’t add pages to a Rails app: a page is a route, a controller action and a view, yours or your agent’s to write.

Site settings

The globe in the top bar opens Site settings. The title, the description, the language, search engines, the favicon and the social image are written as tags in the <head> of a layout: the first template that closes a <head>, in alphabetical order of its path, which is app/views/layouts/application.html.erb in a new app. Images are copied into public/.

What ERB computes there (<title><%= content_for(:title) || "Bakery" %></title>) shows as code and isn’t written. This wasn’t tried on a Rails app.

Styles

With Tailwind 4 in the app, midcode writes Tailwind’s classes (Tailwind CSS). Something has to compile them: see the note about bin/dev above.

Without it, midcode writes mid: utilities and keeps their CSS in public/midcode.css, which Rails serves as it is. On your first style edit, each layout that closes a <head> gets one line:

app/views/layouts/application.html.erb
     <%= stylesheet_link_tag "application" %>+    <link rel="stylesheet" href="/midcode.css">   </head>

It’s one undoable step. Without Tailwind explains what’s in the file.

Limits

  • What helpers build isn’t written as HTML, so it has no mark: link_to, image_tag, button_to, form_with and its fields, content_tag.

  • Haml and Slim views aren’t read. Their pages are listed, and nothing in them can be edited.

  • Routes inside a namespace or a scope are listed without its prefix. Routes made by an engine or in a loop aren’t listed.

  • public/ is never searched for templates: its error pages have a <body> of their own.

  • If the app’s package.json lists Vite (Vite Ruby), midcode opens the folder as a Vite project and its views aren’t matched.

  • A table change made in midcode’s Database isn’t run on a Rails app: midcode hands your agent the steps for a Rails migration (Changing tables).

  • The Rails that was tried ran with a Ruby that isn’t on the PATH, so its command was set in .midcode/server.json. The automatic start runs the same command.

  • The free canvas is for Next.js projects.

Troubleshooting

The server didn’t start, and the log says ruby or bundle wasn’t found. Your login shell doesn’t have the Ruby the app needs. Use Change how it runs on the error card and put it in the command. It’s saved in .midcode/server.json:

.midcode/server.json
{
  "command": "PATH=\"/opt/homebrew/opt/ruby/bin:$PATH\" bin/rails server -b 127.0.0.1 -p $PORT",
  "url": "http://127.0.0.1:$PORT"
}

“A server is already running”. That’s Rails: it keeps one tmp/pids/server.pid per app, so a server you started in a terminal blocks midcode’s. Stop the other one, or give midcode’s its own pid file by adding -P tmp/pids/midcode.pid to the command.

Classes you add from the panel have no effect. The app uses Tailwind and nothing is compiling it. Start your CSS watcher, or use a command that starts everything: midcode also sets PORT in the command’s environment.

A Rails error page. The app is running and something in it failed: pending migrations, a missing credential. Fix it as you would in a browser, then press Reload breakpoints.