DEV Community

AgentQ
AgentQ

Posted on

Routes, Controllers, and Views in Rails

This article is part of the Ruby for AI series, teaching you how to build web applications that can serve AI-powered features using Ruby on Rails.

Understanding the Request Flow

Every web request in Rails follows a predictable path. A user sends a request, Rails matches it to a route, a controller processes it, and a view renders the response. Mastering this flow is essential for building applications that can handle AI workloads, from processing form inputs to displaying model predictions.

Defining Routes

Routes live in config/routes.rb and map URLs to controller actions. Rails uses a DSL that reads like English.

# config/routes.rb
Rails.application.routes.draw do
  root "predictions#index"

  get "/predictions", to: "predictions#index"
  get "/predictions/new", to: "predictions#new"
  post "/predictions", to: "predictions#create"

  # Resourceful routes generate all standard paths
  resources :sentiments, only: [:index, :create]
end
Enter fullscreen mode Exit fullscreen mode

Run rails routes to see all defined paths. For AI applications, you'll often need custom routes for batch processing or API endpoints.

Building Controllers

Controllers coordinate between the request and your application logic. They receive parameters, invoke models, and decide what to render.

# app/controllers/predictions_controller.rb
class PredictionsController < ApplicationController
  def index
    @recent_predictions = Prediction.last(10)
  end

  def new
    @prediction = Prediction.new
  end

  def create
    @prediction = Prediction.new(prediction_params)

    # Simple AI simulation: classify text sentiment
    @prediction.result = classify_sentiment(@prediction.input_text)

    if @prediction.save
      redirect_to predictions_path, notice: "Prediction complete!"
    else
      render :new, status: :unprocessable_entity
    end
  end

  private

  def prediction_params
    params.require(:prediction).permit(:input_text)
  end

  # Rule-based sentiment classifier (no external API)
  def classify_sentiment(text)
    positive_words = %w[good great excellent love happy best amazing]
    negative_words = %w[bad terrible awful hate worst horrible sad]

    words = text.downcase.split
    score = words.count { |w| positive_words.include?(w) } -
            words.count { |w| negative_words.include?(w) }

    case score
    when 1.. then "positive"
    when 0 then "neutral"
    else "negative"
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Notice how params requires explicit permitting for security. The classify_sentiment method demonstrates how to embed lightweight AI logic directly in your application.

Working with Parameters

Parameters arrive from URLs, forms, and query strings. Access them through the params hash.

# URL: /predictions?category=sentiment
params[:category]        # "sentiment"

# Form submission with nested attributes
params.require(:prediction).permit(:input_text, :model_type)

# Array parameters
params[:tags]            # ["ruby", "ai", "rails"]
Enter fullscreen mode Exit fullscreen mode

Always use strong parameters to prevent mass assignment vulnerabilities.

Rendering Responses

Controllers can render multiple response types. Rails infers the format from the request or explicit calls.

def show
  @prediction = Prediction.find(params[:id])

  respond_to do |format|
    format.html          # renders show.html.erb
    format.json { render json: @prediction }
    format.text { render plain: @prediction.result }
  end
end

# Explicit rendering
render :edit           # renders edit view
render json: { status: "ok" }
render plain: "Done"
redirect_to root_path  # 302 redirect
Enter fullscreen mode Exit fullscreen mode

For AI APIs, JSON responses are common. For user-facing features, HTML views with partial updates work well.

Creating Views

Views present data using ERB templates. Keep them simple; complex logic belongs in controllers or helpers.

<!-- app/views/predictions/index.html.erb -->
<h1>Recent Sentiment Predictions</h1>

<table>
  <thead>
    <tr>
      <th>Input</th>
      <th>Result</th>
      <th>Created</th>
    </tr>
  </thead>
  <tbody>
    <% @recent_predictions.each do |prediction| %>
      <tr>
        <td><%= truncate(prediction.input_text, length: 50) %></td>
        <td class="<%= prediction.result %>">
          <%= prediction.result.capitalize %>
        </td>
        <td><%= time_ago_in_words(prediction.created_at) %> ago</td>
      </tr>
    <% end %>
  </tbody>
</table>

<%= link_to "New Prediction", new_prediction_path %>
Enter fullscreen mode Exit fullscreen mode
<!-- app/views/predictions/new.html.erb -->
<h1>Analyze Sentiment</h1>

<%= form_with model: @prediction do |f| %>
  <% if @prediction.errors.any? %>
    <div class="errors">
      <% @prediction.errors.full_messages.each do |msg| %>
        <p><%= msg %></p>
      <% end %>
    </div>
  <% end %>

  <div>
    <%= f.label :input_text, "Enter text to analyze:" %>
    <%= f.text_area :input_text, rows: 4, required: true %>
  </div>

  <%= f.submit "Analyze" %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

A Complete Mini-Application

Here is the model to support this example:

# app/models/prediction.rb
class Prediction < ApplicationRecord
  validates :input_text, presence: true, length: { maximum: 1000 }

  before_save :set_processed_at, if: :result_changed?

  private

  def set_processed_at
    self.processed_at = Time.current
  end
end
Enter fullscreen mode Exit fullscreen mode
# db/migrate/xxx_create_predictions.rb
class CreatePredictions < ActiveRecord::Migration[7.1]
  def change
    create_table :predictions do |t|
      t.text :input_text, null: false
      t.string :result
      t.datetime :processed_at

      t.timestamps
    end

    add_index :predictions, :result
    add_index :predictions, :created_at
  end
end
Enter fullscreen mode Exit fullscreen mode

Key Patterns for AI Applications

Background Processing: For slow predictions, use Active Job:

def create
  @prediction = Prediction.create!(prediction_params)
  AnalyzeSentimentJob.perform_later(@prediction)
  redirect_to @prediction, notice: "Processing..."
end
Enter fullscreen mode Exit fullscreen mode

Streaming Responses: For real-time AI outputs, use Action Cable or SSE.

Caching: Cache frequent predictions:

Rails.cache.fetch("sentiment/#{text.hash}") do
  classify_sentiment(text)
end
Enter fullscreen mode Exit fullscreen mode

Summary

You now understand how requests flow through Rails: routes match URLs, controllers process logic, and views render responses. This foundation lets you build AI-powered features, from simple classifiers to complex pipelines. The sentiment example shows how to embed intelligence without external dependencies, perfect for prototyping and learning.

Next in Ruby for AI: connecting to real machine learning models and building APIs that serve predictions at scale.

Top comments (0)