ctp
ctp

Reputation: 1087

Ruby EventMachine & functions

I'm reading a Redis set within an EventMachine reactor loop using a suitable Redis EM gem ('em-hiredis' in my case) and have to check if some Redis sets contain members in a cascade. My aim is to get the name of the set which is not empty:

require 'eventmachine'
require 'em-hiredis'

def fetch_queue
  @redis.scard('todo').callback do |scard_todo|
    if scard_todo.zero?
      @redis.scard('failed_1').callback do |scard_failed_1|
        if scard_failed_1.zero?
          @redis.scard('failed_2').callback do |scard_failed_2|
            if scard_failed_2.zero?
              @redis.scard('failed_3').callback do |scard_failed_3|
                if scard_failed_3.zero?
                  EM.stop
                else
                  queue = 'failed_3'
                end 
              end 
            else
              queue = 'failed_2'
            end 
          end 
        else
          queue = 'failed_1'
        end 
      end 
    else
      queue = 'todo'
    end 
  end 
end

EM.run do
  @redis = EM::Hiredis.connect "redis://#{HOST}:#{PORT}"

  # How to get the value of fetch_queue?
  foo = fetch_queue
  puts foo
end

My question is: how can I tell EM to return the value of 'queue' in 'fetch_queue' to use it in the reactor loop? a simple "return queue = 'todo'", "return queue = 'failed_1'" etc. in fetch_queue results in "unexpected return (LocalJumpError)" error message.

Upvotes: 1

Views: 738

Answers (1)

raggi
raggi

Reputation: 1297

Please for the love of debugging use some more methods, you wouldn't factor other code like this, would you?

Anyway, this is essentially what you probably want to do, so you can both factor and test your code:

require 'eventmachine'
require 'em-hiredis'

# This is a simple class that represents an extremely simple, linear state
# machine. It just walks the "from" parameter one by one, until it finds a
# non-empty set by that name. When a non-empty set is found, the given callback
# is called with the name of the set.
class Finder

  def initialize(redis, from, &callback)
    @redis = redis
    @from = from.dup
    @callback = callback
  end

  def do_next
    # If the from list is empty, we terminate, as we have no more steps
    unless @current = @from.shift
      EM.stop # or callback.call :error, whatever
    end

    @redis.scard(@current).callback do |scard|
      if scard.zero?
        do_next
      else
        @callback.call @current
      end
    end
  end

  alias go do_next

end

EM.run do
  @redis = EM::Hiredis.connect "redis://#{HOST}:#{PORT}"

  finder = Finder.new(redis, %w[todo failed_1 failed_2 failed_3]) do |name|
    puts "Found non-empty set: #{name}"
  end

  finder.go
end

Upvotes: 9

Related Questions