Skip to content
Dave Strus edited this page Jul 16, 2015 · 1 revision

In this unit, we'll explore the following topics:

  • Making DELETE requests
  • Ask users to confirm before following a link

Only one bit of CRUD left! Let's get ready to delete posts.

The controller bit looks a lot like what we've already been doing.

LAB

Implement PostsController#destroy.

SOLUTION

We can do something very similar to what we did for update.

  def destroy
    post = Post.find params[:id]
    if post.destroy
      redirect_to posts_path, flash: { notice: 'Your post has been removed.' }
    else
      redirect_to posts_path, flash: { error: 'We were unable to remove that post.' }
    end
  end

Now, how to actually hit this method? There is no delete_post_path helper. Let's look at our routes.

$ bin/rake routes
   Prefix Verb   URI Pattern               Controller#Action
     root GET    /                         posts#index
    posts GET    /posts(.:format)          posts#index
          POST   /posts(.:format)          posts#create
 new_post GET    /posts/new(.:format)      posts#new
edit_post GET    /posts/:id/edit(.:format) posts#edit
     post GET    /posts/:id(.:format)      posts#show
          PATCH  /posts/:id(.:format)      posts#update
          PUT    /posts/:id(.:format)      posts#update
          DELETE /posts/:id(.:format)      posts#destroy

Deletion uses the same URL as show and update. The difference is in in the HTTP verb, which must be DELETE.

The link_to helper provides an option for specifying the HTTP method (as HTTP verbs are formally known; method just happens to be an overloaded word in object-oriented programming).

link_to 'delete', post_path(@post), method: :delete

Let's add something like that to posts/index.

app/views/posts/index.html.erb

    <ul class="actions">
      <li><%= link_to 'edit', edit_post_path(post) %></li>
      <li><%= link_to 'delete', post_path(post), method: :delete %></li>
    </ul>

From the documentation:

method: symbol of HTTP verb - This modifier will dynamically create an HTML form and immediately submit the form for processing using the HTTP verb specified. Useful for having links perform a POST operation in dangerous actions like deleting a record (which search bots can follow while spidering your site). Supported verbs are :post, :delete, :patch, and :put. Note that if the user has JavaScript disabled, the request will fall back to using GET.

Try it out in your browser, and you should be able to delete posts. In fact, you can delete them alarmingly quickly. We can add a confirmation prompt by adding the data-confirm attribute to the link. With the link_to helper, data- attributes can be set in a hash, like so:

link_to 'delete', post_path(@post), method: :delete, data: { confirm: 'Are you sure? This cannot be undone.' }

Let's add the confirmation to our view.

app/views/posts/index.html.erb

    <ul class="actions">
      <li><%= link_to 'edit', edit_post_path(post) %></li>
      <li><%= link_to 'delete', post_path(post), method: :delete, data: { confirm: 'Are you sure? This cannot be undone.' } %></li>
    </ul>

Try it again, and it should be bit less alarming.

We'll want the same link to appear on posts/show. In fact, the overlap between these two views is beginning to get ridiculous.

LAB: IT'S PARTIAL TIME

Extract the common elements from posts/index and posts/show into one or more partials.

SOLUTION:

I created two partials: slug, to hold the title and tagline; and actions to hold the action links. Both partials require a Post object to be passed as a local named post.

_app/views/posts/slug.html.erb

<div class="title"><a href="<%= post_url(post) %>"><%= post.title %></a></div>
<div class="tagline" title="<%= post.created_at %>">submitted <%= time_ago_in_words post.created_at %> ago</div>

_app/views/posts/actions.html.erb

<ul class="actions">
  <li><%= link_to 'edit', edit_post_path(post) %></li>
  <li><%= link_to 'delete', post_path(post), method: :delete, data: { confirm: 'Are you sure? This cannot be undone.' } %></li>
</ul>

app/views/posts/index.html.erb

<ul class="posts">
<% @posts.each do |post| %>
  <li>
    <%= render 'slug', post: post %>
    <%= render 'actions', post: post %>
  </li>
<% end %>
</ul>

app/views/posts/show.html.erb

<article class="post">
  <%= render 'slug', post: @post %>
  <section class="body"><%= @post.body %></section>
  <%= render 'actions', post: @post %>
</article>

If all is well, index and show should both look and behave exactly as they did before.