Advice on avoiding duplication between controller actions

I have a Rails application that allows creating new records without leaving the index page. Turbo makes it easy to update the page after creating a record, but I’m wondering how best to avoid duplication between index and create actions. My index page shows a table with the records sorted in a particular order. I could use a Turbo Stream response to append the new record as a row in the table. However, I need to re-render the entire table in order to preserve the sort order. I don’t like putting “index action logic” in the create action simply to render some parts of the index page. This type of thing happens so often in our application that I’m wondering if there is a best practice that I am missing.

Example:

# widgets_controller.rb
def index
  @widgets = Widget.all.order(:name)
end

def create
  @widget = Widget.new(widget_params)
  if @widget.save
    flash.now.notice = "Widget created successfully."
    @widgets = Widget.all.order(:name) # need to add this again because we are replacing the widgets list
  else
    render :new
  end
end

# widgets/create.turbo_stream.erb
<%= turbo_stream.replace :widgets, partial: "widgets", locals: {widgets: @widgets} %>
<%= turbo_stream.replace :flash, partial: "layouts/flash" %>

Aside from moving the “all widgets” logic into a shared private method, is there some sort of best practice for separating index and create/update concerns when using Turbo for this kind of “edit in place” experience?

I don’t see what you mean by “moving the all widgets logic into a shared private method” as I think the problem is implementing logic where it is not desired, rather making it dry. Though you may use the before stream action to add your new widget at the right place : Turbo Reference
This makes you find out after what ID to insert your new record but I agree this is a shame to re-render your whole table just to insert an element. Especially if this table is big.

Or maybe implement the ordering in the view ? I guess the controller is rather pulling the right data rather than formatting it for display, so it would be acceptable.

Your case is actually different from the general use cases, as most apps order through IDs or created_at either asc and desc, (which makes it easy to use append or prepend stream actions) then I guess you can’t avoid the extra work and I am not sure there is a proper way to do it.