Sinatra: Accessing Data and Methods with {include:}
Christopher Ninman
Posted on February 10, 2022
So you've been using ActiveRecord/Sinatra for your backend and have come up with a clever method to filter your database and achieve world peace. Or maybe you just created a Class
method to calculate the average rating of a particular location in your app, like me. Good for you!!! But now what? How can you access that brilliant/less brilliant but useful information?
My Project
I like to have something tangible as an example when trying to learn a new concept. So here's a brief scenario (part of a project that I created during bootcamp) to be our spirit guide:
The backend contains these Model
s:
City has_many :users, has_many: locations
User belongs_to :city, has_many :reviews, has_many :locations, through: :reviews
Location belongs_to :city, has_many :reviews, has_many :users, through: :reviews
Review belongs_to :location, belongs_to :user
Each user can write a review for a location. This review includes a rating, which is an integer
between 1 and 5.
Several Users have written Reviews for the Location, so now our location has several Reviews. Luckily, ActiveRecord has given us a very non-original, but ridiculously simple, way to calculate a Location's rating.
Class.average(:attribute)
Creating Our Method
Time to put the Class.average
method to use in our current scenario
class Location < ActiveRecord::Base
belongs_to :city
has_many :reviews
has_many :users, through: :reviews
def average_rating
self.reviews.average(:rating).to_f
end
end
// Assuming you'll want to use that average as a decimal,
// we're calling `to_f` on our 'average_rating'
Previously we had access to instance methods on instances of locations, such as Location.first.reviews
, Location.first.users
, Location.first.reviews.first
, etc. Now we have an additional method we can call on our Location instances: Location.first.average_rating
. This will return us the average of all the ratings that have been given in the Reviews of that instance.
How Can I Get That to My Front-End?
Let's begin by taking a look at our Locations Controller, and what we can access when we make a fetch request to our backend.
class LocationsController < ApplicationController
def serialize_locations(objects)
objects.to_json(include: [:reviews, :users, :city])
end
get '/locations' do
serialize_locations(Location.all)
end
end
// Rather than calling
// Location.all.to_json(include: [:.......]),
// we are using the serialize_locations function to do
// that work, as we may want to reuse that logic in other
// fetch requests we make, such as '/locations/:id', or
// post, patch, and delete requests
Since we may want access to the Location instance's reviews, users, and city information, we're using include:
and an array of it's connected models. We could now call something like Location.first.city.name
and have access to the name of the city where the Location belongs.
The problem is, we still can't access the average rating of a Location instance.
Time to Access the Method
We can dig further and further into our data and create nested routes using include:
, example: calling include
inside of the Location's Users and including the Location's User's Review. Check out this great post for more info.
But there is something else that we can include: methods
class LocationsController < ApplicationController
def serialize_locations(objects)
objects.to_json(include: [:reviews, :users, :city], methods: :average_rating)
end
get '/locations' do
serialize_locations(Location.all)
end
end
We have included an array of related tables, along with the named method (average_rating) inside our Locations class.
id: 1,
location_name: "Prospect Park",
city_id: 2,
neighborhood: "Paradise Creek",
times_visited: 8,
average_rating: 3.25,
+reviews: [...],
+users: [...],
+city: {...}
Oh, you actually have several methods that you created in your Location class that you want? Let's include an array of methods!
def serialize_locations(objects)
objects.to_json(include: [:reviews, :users, :city], methods: [:average_rating, :sum_times_visited])
end
Posted on February 10, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.