Ask A Question

Notifications

You’re not receiving notifications from this thread.

Refactoring controllers to keep them RESTful

Nino Rosella asked in Rails

Hi gang

I have a feature where a user can query the Google Books API with an ISBN number and get the book as a response. I have this all working nicely, but I'd like to refactor my Books controller to keep it restful.

Currently, my code looks like this:
routes.rb:

resources :books do
  collection do
    post :submit_book
    get :try_again
  end
end

BooksController.rb:

###
def submit_book
  @book = Book.new
  @book.authors.build

  isbn = Book.sanitised_isbn(params[:q])

  begin isbn
    @response = GoogleBooks.new(isbn)
  rescue => e
    redirect_to try_again_books_path(isbn: params[:q])
    nil
  end
end
###

_form.html.erb

<%= form_tag({controller: "books", action: "submit_book", method: "post"}) do %>
  <%= text_field_tag :q, "", placeholder: "ISBN-10 or ISBN-13" %>
  <%= submit_tag("Lookup book", name: nil) %>
<% end %>

What I'm trying to do now is to extract the logic from the submit_book method into it's own controller, but I'm having difficulty making it work. I'm getting bogged down in the weeds for sure. Anyone got any pointers on where I should be going?

When I submit this new form, my browser downloads an empty file lol

routes.rb:

resource :google_book, only: [:create]
resources :books

google_books_controller.rb:

class GoogleBooksController < ApplicationController
  def create
    @book = Book.new
    @book.authors.build

    isbn = Book.sanitised_isbn(params[:q])

    begin isbn
      @response = GoogleBooks.new(isbn)
    rescue => e
      redirect_to try_again_books_path(isbn: params[:q])
      nil
    end
  end
end

_form.html.erb:

<%= form_tag({controller: "google_books", action: "create", method: "post"}) do %>
  <%= text_field_tag :q, "", placeholder: "ISBN-10 or ISBN-13" %>
  <%= submit_tag("Lookup book", name: nil) %>
<% end %>
Reply

Got it working. Will post the answer here in case anyone has the same question in the future.

My _form.html.erb became:

<%= form_with url: google_book_path, local: true do %>
  <p><small>Enter the ISBN (excluding spaces and hyphens)</small></p>
  <div class="form-group">
    <%= text_field_tag :q, "", placeholder: "ISBN-10 or ISBN-13", required: true, class: "form-control" %>
  </div>
  <%= submit_tag("Lookup book", name: nil, class: "btn btn-dark") %>
<% end %>

And I was forgetting that I obviously needed the appropriate view, so I added /views/google_books/create.html.erb

I was then able to remove the submit_book method from BooksController and everything just feels much cleaner and organised.

Reply
Reply

Thanks for sending that over 👍 That's an even cleaner solution.

Ok, so now my code looks like the following:

routes.rb:

namespace :books do
  resource :google_book, only: [:create]
end

resources :books

controllers/books/google_book.rb:

class Books::GoogleBooksController < ApplicationController
  def create
    @book = Book.new
    @book.authors.build

    isbn = Book.sanitised_isbn(params[:q])

    begin isbn
      @response = GoogleBook.new(isbn)
    rescue => e
      render 'books/google_books/try_again'
      nil
    end
  end
end

views/books/google_books/_form.html.erb:

<%= form_with url: books_google_book_path, local: true do %>
  <%= text_field_tag :q, "", placeholder: "ISBN-10 or ISBN-13" %>
  <%= submit_tag("Lookup book", name: nil) %>
<% end %>

"And what I found is that the freedom that gives you is that each controller now has its own scope with its own sets of filters that apply..." - DHH

Indeed.

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 76,990+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more.

    © 2023 GoRails, LLC. All rights reserved.