Lesson Tuesday

Now that we have our backend logic in place, we can add routes and views for song CRUD functionality. Let's start by taking a look at the code for our new routes.

Adding Nested Routes


app.rb
...
require('./lib/song')
...

# Get the detail for a specific song such as lyrics and songwriters.
get('/albums/:id/songs/:song_id') do
  @song = Song.find(params[:song_id].to_i())
  erb(:song)
end

# Post a new song. After the song is added, Sinatra will route to the view for the album the song belongs to.
post('/albums/:id/songs') do
  @album = Album.find(params[:id].to_i())
  song = Song.new(params[:song_name], @album.id, nil)
  song.save()
  erb(:album)
end

# Edit a song and then route back to the album view.
patch('/albums/:id/songs/:song_id') do
  @album = Album.find(params[:id].to_i())
  if params[:name] != ""
    song = Song.find(params[:song_id].to_i())
    song.update(params[:name], @album.id)
  end
  erb(:album)
end

# Delete a song and then route back to the album view.
delete('/albums/:id/songs/:song_id') do
  song = Song.find(params[:song_id].to_i())
  song.delete
  @album = Album.find(params[:id].to_i())
  erb(:album)
end

First we need to make sure we require our Song logic so Sinatra can access it. Next, we add four new routes for getting individual songs as well as creating, updating, and deleting songs. Note that we don't add a route for looking at an album's songs — while we could do that, we might as well just add the songs to the album detail page instead.

Our '/albums/:id/songs/:song_id' route is straightforward — we just find the song we need for our view.

To post to '/albums/:id/songs', we need to find an Album by id. Once we do that, we can associate the song we've created in our form to the specific Album we've found.

Our patch route involves finding both an Album and a Song. This allows us to update a specific Song and also redirect to the Album to which the Song belongs. We also make sure to verify whether the user input is empty or not, and only update the song's name if it is not empty.

Our delete route finds the Song to be deleted, utilizes our delete() method and then routes back to a specific Album page.

Updates to Views


We only need to add one new file: song.erb. All of our new view code will either go in song.erb or album.erb. Let's take a look at both:

views/album.erb
<h2>Album Name: <%= @album.name %></h2>

<h3>Album Songs</h3>

  <% if @album.songs.any? %>
    <ul>
      <% @album.songs.each do |song| %>
        <li><a href="/albums/<%= @album.id %>/songs/<%= song.id %>"><%= song.name %></a></li>
      <% end %>
    </ul>
  <% else %>
    <p>There are no songs listed for this album yet.</p>
  <% end %>

<h4>Add a song:</h4>

<form action="/albums/<%= @album.id %>/songs" method="post">
  <div class="form-group">
    <label for="song_name">Song name</label>
    <input id="song_name" name="song_name" class="form-control" type="text">
  </div>
  <button type="submit" class="btn btn-success">Add song</button>
</form>

<p><a href="/albums/<%= @album.id %>/edit">Edit album</a></p>
<p><a href="/albums">Return to album list</a></p>

First we add a loop for an album's songs. If there are any songs, they should be displayed along with a link to the route for an individual song. We also include a form for adding songs directly to album.erb. We could have a separate page with a form but this is more convenient for users.

Let's take a look at our new song.erb file:

views/song.erb
<h2>Song: <%= @song.name %></h2>
<h3>Album: <%= @song.album.name %></h3>

<form action="/albums/<%= @song.album.id %>/songs/<%= @song.id %>" method="post">
  <input name="_method" type="hidden" value="patch">

  <label for="name">Rename song</label>
  <input id="name" name="name" type="text">

  <button type="submit" class="btn btn-success">Update song</button>
</form>

<form action="/albums/<%= @song.album.id %>/songs/<%= @song.id %>" method="post">
  <input name="_method" type="hidden" value="delete">

  <button type="submit" class="btn btn-danger">Delete song</button>
</form>

<a href="/albums/<%= @song.album.id %>">Back to <%= @song.album.name %></a>

We add our form for updating and deleting a song to this view. This is also where we'd show any specific details about an individual song that don't belong in the view for a specific album.

We now have a basic application for adding full CRUD functionality for both albums and songs.

Lesson 27 of 37
Last updated August 7, 2022