Refactoring controllers to keep them RESTful
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 %>
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.
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.