I’ve been rather reflective this past week. This is mostly because the end of this year of technical Tuesdays is now very much in sight, with only a handful more posts left to write. Also, I’ve been going back over old posts and correcting a few spelling and code snippet mistakes that have been brought to my attention (shoutout to all of you who have been proofreading for me!). All of this is to say that I never realized until recently that I’ve covered quite the spread of different topics over the past year!
But here’s the rub: I’m not even close to being done with my list of things I still want to learn more about. And even though that list keeps growing, I’ve noticed that the complexity behind the concepts I’m learning and writing about has begun to slowly change. While I started off focusing on syntax and DSL-specific topics, now those topics have become more theoretical in nature. While I used to write about things like the Rails group_by method and the ampersand operator, now I’m diving into more complex concepts like association callbacks and service objects.
Extra extra! Read all about pub-sub
In the context of building out systems of software, the publish-subscribe pattern is a way of handling how messages are sent between objects. We are probably already familiar with the concept of the “single responsiblity principle”, or the idea that no method should be responsible for more than one thing. This same concept extends to other parts of our application as well. As we’ve learned through the process of refactoring, our controllers shouldn’t be responsible for the logic that really belongs in model. Similarly, a model shouldn’t be responsible for calling on a third-party service or performing some task or piece of logic that doesn’t really relate to its own state.
The way that we solve this in Ruby is by abstracting out logic into smaller components. We have service objects, which are responsible for carrying out tasks and therefore are easily-testable, and encapuslate a very specific piece of functionality that the rest of the application doesn’t really need to know about.
In Ruby, when we have two objects that are connected in some way — for example, a
Human — and the state of the
Human changes, we probably want to notify the instance of the
Dog that the object is associated with. We could say that the
Human sends out a “message” to the objects that are “listening” to it. The real terms that we are trying to use here are publish and subscribe. An instance of a
Human object “publishes” events (i.e., the human
is_ready_to_play, etc.), and the
Dog object listens and “subscribes” to these events (and probably behaves accordingly, aka it would
jump_excitedly when the human
Usually, for smaller applications, it’s fine to just rely on one object telling another to behave a certain way explicitly. But, things get kind of messy as you have more objects “listening” to the events of other objects. This is where our knowledge of service objects can come in handy. We can pretty easily abstract out units of work into service classes. But, this still means that we need to notify our service classes whenever they need to change; in other words, we have to tell our services Hey, you need to behave in a certain way because something about the object you’re associated with has changed!
The publish-subscribe pattern uses the exact same concept of sending messages between objects when something about one of the objects changes — however, it does this by using an intermediary object, sometimes called a message broker or an event bus. The important thing here is that the object that does the “publishing” or “broadcasting” of an event has no idea who is listening to its events. It just sends out a signal of sorts. The intermediary message broker object then is responsible for knowing who is “subscribed” to this event, and who needs to know about it. The message broker then makes sure that the correct object gets this message. In the simple example from above, a
Human might publish an event, and another object, such as a
DogNotifer, would be responsible for telling the
Dog instance that it needs to do something.
I really like the way that Ahmed Abdel Razzak explains this in his blog post:
“The publish-subscribe pattern is a Ruby on Rails messaging pattern where senders of messages (publishers), do not program the messages to be sent directly to specific receivers (subscribers). Instead, the programmer “publishes” messages (events), without any knowledge of any subscribers there may be. The pub-sub is a pattern used to communicate messages between different system components without these components knowing anything about each other’s identity.”
This concept can be a little tricky to understand in Ruby until you see all the classes in action. So let’s start publishing and subscribing!
Hush, don’t shout
There are a few different pub-sub gems out there, but the one that I’ve found the easiest to use is a gem called
We’ll start the same way that we always do: by adding
gem 'wisper' to our
Gemfile, and then running the
Now, let’s take a look at one of our Ruby classes that we can implement the pub-sub pattern on. Here we have a
PressReview class, that is a representation of a book review that might generate a lot of press for an
Author in our bookstore app. These press reviews are pretty important (think the New York Times Bestseller List, etc.), so we want to notify the author of the book when the press review goes live. We also want to generate a tiny news snippet that will just have a few lines about the article once it has been created:
1 2 3 4 5 6 7 8 9 10 11 12 13
The first thing that we’ll want to do to add the
wisper gem is to include the
Wisper::Publisher module into the class that is going to be broadcasting events. In this case, we want to broadcast an event when our
PressReview class has been successfully created and has gone “live”. Let’s create a message broker class called
CreatePressReview that will handle the broadcasting of this event. We will either need to include
Wisper::Publisher or alternatively,
1 2 3
Next, we’ll need to add the method that is going to be doing the “broadcasting” of the event. It’s pretty typical to use a
press_review is created succesfully, or if it fails to be created:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
We’ll notice that this class takes a
press_review_id, and then contains the logic to set and check whether the
press_review we just found is
live or not. If it is live and we’re ready to notify our author and generate our news item, we’re calling the
broadcast method, and passing it the name of the function we want to execute, along with the
press_review instance. And if the
press_review isn’t live, we’re calling a different method isntead.
It’s worth noting that the
broadcast method is also aliased to
announce, so either of these lines would have also worked:
Before we add any listener objects that will subscribe to these events, let’s first abstract out those
generate_news_item private methods from our
PressReview class into services objects. Our
alert_author method can now be rewritten as a
AuthorAlerter Plain Old Ruby Class, which calls upon an
1 2 3 4 5 6 7
1 2 3 4 5 6
generate_news_item method can be refactored into a
NewsItemGenerator service class, that creates a new instance of a
1 2 3 4 5 6 7
1 2 3 4 5
Now that we have our publishers in place, we need to make our service objects actually “listen” to these events.
How And Why To Wisper
Our event listeners will subscribe at runtime to their publishers, which means that they won’t be executed until the broadcast events are actually invoked.
We can make any object a listener that subscribes to broadcast events by calling the
subscribe method. So, inside of our controller, we could do something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13
We’ll remember that it’s the
CreatePressReview intermediary event bus class that’s actually responsible for broadcasting our events now, not the callbacks in the
PressReview class like we had before! We’re making sure that our
NewsItemGenerator services are subscribed to the “sucess” and “failure” events of the
call method that is defined in our
CreatePressReview intermediary class. And it’s only when we invoke the
call method (in the last of this controller action) that we’re “broadcasting” our event. We’ve hooked up everything in such a way that the event bus class and the service objects will run the correct code if our
press_review instance actually goes live.
But we’re not limited to doing all of this inside of a controller action! If we wanted to do this directly from our Rails model itself, we could write some similar logic by specificing the methods we want to invoke if the
PressReview was created successfully:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
This rewrite has helped us divide our code into smaller, discrete classes that are easily-testable. In fact, we could use the
wisper-rspec gem to help us in stubbing out some tests!
The pub-sub pattern might not be for everyone, but it’s certainly interesting to read and learn about. Even you decide to never use it, at least you can say that you saw a really cute penguin do some serious subscribing at the end of this post:
- The publish-subscribe pattern is a way to abstract out the conveying of messages between objects. The
wispergem is useful for implementing pub-sub in Ruby.
- Want to read more about the pub-sub pattern in Rails? Check out this great blog post.
wispergem has changed a bit over time, so there are a few good write-ups on how to implement it. Check one out here and here.