At work recently, we needed to introduce the concept of a queue into our rails app. MySQL really doesn’t do a great job with these types of things, because it is so slow. Redis is a much better solution, because its speed and ease of use.

I built the queueing concern on top of RedisObjects, because it has such a great DSL wrapper for storing ruby types into redis. The main feature I use is their list object.

```ruby queueable.rb module Queueable extend ActiveSupport::Concern

included do include Redis::Objects end

# more code here

end

This adds Redis::Objects to the models that I use this concern on so I can leverage their list DSL method.

In my model, I want to write my own DSL to make it super easy for me to create new queue attributes in all of my models.  I want to be able to just do this in my model:

```ruby user.rb
class User < ActiveRecord::Base
  include Queueable

  queue :scoring_queue
  queue :email_queue

end

For the purposes of my application, I needed to be able to:

  • rotate the queue, such that the top element returned and is moved to the bottom of the queue.
  • set the entire queue to something new.
  • re-queue a element, so that if it exists in my queue, it gets sent to the bottom.

ruby user_controller.rb class UserController < ActiveController::Base def show user = User.find(params[:id]) next_score = user.rotate_scoring_queue #gets the top element and moves it to the bottom user.requeue_scoring_queue invited_score # moves this score to the bottom of the queue end end

To accomplish this, I use a meta programming to dynamically create these methods. The define_method in ruby is an extremely powerful concept that allows developers to create methods based with dynamic names.

```ruby queueable.rb class_methods do def queue(queue_name) list queue_name

  define_method("rotate_#{queue_name}") do
    queue_list = send(queue_name)
    queue_list.rpoplpush queue_list
  end

  define_method("#{queue_name}=") do |vals|
    queue_list = send(queue_name)
    Redis::Objects.redis.multi do
      queue_list.clear
      queue_list.push *vals.reverse if vals.length > 0
    end
  end

  define_method("requeue_#{queue_name}") do |target|
    queue_list = send(queue_name)
    number_removed = queue_list.delete target
    return if number_removed == 0
    
    last_item_in_queue = queue_list.first

    if last_item_in_queue
      queue_list.insert :before, last_item_in_queue, target
    else
      queue_list.push target
    end
  end ```

The first line def queue adds in my DSL method to the model to allow queues with a given name to be created.

Unfortunately RedisObjects doesn’t come with an out of the box way to set an entire list at once. So I had to write my own using Redis transactions. So the second method overrides the setter method of the list to accept an array of values that we will set.

The re-queue method removes a target value from the queue. Then it sets it to the queue, if it can’t find a last item (then that means it was the only item in the queue), it adds it back to the list.