academy

Debugging exceptions in Rails

Jeff Kreeftmeijer

Jeff Kreeftmeijer on

Debugging exceptions in Rails

When an error happens in your Rails application, the exception and stack trace help you find where the problem occurred. After knowing what happened where, we need to find out why it happened. In this article, we'll go over using the backtrace to find a bug in a Rails application.

1NoMethodError (undefined method `request_uri' for #<URI::Generic >):
2
3app/models/product.rb:8:in `download_image!'
4app/controllers/products_controller.rb:5:in `create'

In this example exception, we received a NoMethodError with undefined method `request_uri' for #<URI::Generic > as its message. Since this exception doesn't immediately tell us what the problem is, we'll need to inspect the stack trace to find out what happened.

1app/models/product.rb:8:in `download_image!'
2app/controllers/products_controller.rb:5:in `create'

Looking at the stack trace, we learn the exception was raised from a download_image! method on the Product model. We'll continue our investigation in the code, and we'll work our way down the stack trace to find out what's going wrong.

Opening the model shows that line 8 (where the exception was raised from) calls Net::HTTP.get(uri), so it looks like that uri is not the object we expect it to be.

1require 'net/http'
2
3class Product < ApplicationRecord
4  after_save :download_image!
5
6  def download_image!
7    uri = URI(image_url)
8    contents = Net::HTTP.get(uri)
9
10    File.open("public#{local_image_path}", 'wb') do |file|
11      file.write contents
12    end
13  end
14
15  def local_image_path
16    "/product_#{id}.png"
17  end
18end

Since the download_image! method is an after_save callback, we know it's executed immediately after saving a new Product.

The uri variable is built from a method named image_url on line 7. To find out where that comes from, we'll take another look at the stack trace to see the Product#create method is called from ProductsController#create.

1class ProductsController < ApplicationController
2  def create
3    @product = Product.new(product_params)
4
5    if @product.save
6      redirect_to @product, notice: 'Product was successfully created.'
7    else
8      render :new
9    end
10  end
11
12  private
13    def product_params
14      params.require(:product).permit(:title, :description, :image_url, :price)
15    end
16end

Aha! ProductsController#create creates a new product with the product_params, which include the :image_url parameter we were looking for.

We know the image_url attribute is used to build the broken URI. If we leave the image_url field empty when creating a new product, we can successfully reproduce the problem.

In this case, creating URI with an empty string as its value results in a URI::Generic object instead of a URI::HTTP, because it can't determine the URL's format. Since the former doesn't have a #request_uri method, it raises a NoMethodError from Net::HTTP.get.

Depending on the project's requirements, adding a validation to make sure the field is not empty could fix the issue. Only validating to make sure the image URL is not empty won't fix all possible problems with this implementation (we'll still get an exception when the passed value is not an URL, for example), but it's a good start.

Tracking down exceptions using the stack trace

Rails' logs provide a great way to debug issues. Although the raised exceptions don't always make a lot of sense on first glance, carefully retracting the steps the code took to get to the issue is usually a great way to find out what went wrong, even if the source of the problem is buried a little deeper in your app.

We’d love to know how you liked this article, if you have any questions about it, and what you’d like to read about next, so be sure to let us know at @AppSignal.

Share this article

RSS

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps