How Should My Rails App Talk to Redis?

While you’re writing your Rails app, you might run into a problem that you could solve more easily with a different data store. For example, you might have a leaderboard that ranks users by the number of points they got for answering questions on your site. With Redis Sorted Sets, a lot of the implementation is done for you. Awesome! But where do you put the code that interacts with Redis?

Your User model could talk to Redis:

class User < ActiveRecord::Base
  def award_points(points)
    Redis.current.zincrby("leaderboard", points, id)
  end
end

But now your User model holds responsibility for representing a user, talking to Redis, and managing the leaderboard! This makes User harder to understand and harder to test, and that’s the opposite of what you want.

Instead, you could wrap the Redis requests in a new object that represents what you’re using Redis for. For example, you could create a Leaderboard class that wraps the Redis communication, doesn’t inherit from ActiveRecord::Base, but still lives in the app/models directory. This would look like:

app/models/leaderboard.rb
class Leaderboard
  def award_points_to_user(user_id, points)
    Redis.current.zincrby("leaderboard", points, user_id)
  end
end
app/models/user.rb
class User < ActiveRecord::Base
  def award_points(leaderboard, points)
    leaderboard.award_points_to_user(id, points)
  end
end

Both of these classes can live in app/models, but now you’re not contaminating your ActiveRecord models with extra logic. The Leaderboard class manages the communication with Redis, so the User class no longer has to care about how leaderboards are implemented. This also makes it easier to test.

You gain a lot by creating new classes for new responsibilities, and app/models is a great place to keep them. You get the benefits of leaning on another service for your feature’s implementation, while also keeping your code easy to work with.

Try it out

Can you think of any network service communication that’s called directly from your ActiveRecord models, controllers, or views? Try moving that code into a non-ActiveRecord data model, and see if your code becomes easier to understand, work with, and test. Then, send me an email and let me know how it went!

Just remember:

All problems in computer science can be solved by another level of indirection.

— David Wheeler

…except for the problem of too many layers of indirection.

— Kevlin Henney

Pushing through tutorials, and still not learning anything?

Have you slogged through the same guide three times and still don't know how to build a real app?

In this free 7-day Rails course, you'll learn specific steps to start your own Rails apps — without giving up, and without being overwhelmed.

You'll also discover the fastest way to learn new Rails features with your 32-page sample of Practicing Rails: Learn Rails Without Being Overwhelmed.

Sign up below to get started:

Powered by ConvertKit

Did you like this article? You should read these:

Comments