hdauven hdauven - 1 month ago 8
Ruby Question

Clean implementation for multiple decisions in ruby

background

I'm writing an API that processes data from an external application. My application processes JSON responses from the external application and offers relevant information to consumers of my service. (These consumers are internal to my organisation)

The external application has an API that allows me to check for updates. Updates are triggered by events. The API offers 12 different types of events. The event types are offered in a string format. (ex. 'MoveEvent', 'DeleteEvent', 'CreateEvent')

I need to write a specific processing algorithm for each event.

Problem

I'm looking for a clean, DRY and SOLID way to implement the event processing system. The focus for this application is on code quality and a solid architecture.

My solution and thoughts

There are a number of ways to tackle this issue, but my best guess so far has been:


  • Create a hash that holds the string name of the event types and map them to a processing class.

  • Use the Strategy pattern to define a common interface for all the processing classes to adhere to, so that any mediating class only needs to know the message to which the processing classes can respond.

  • Use some sort of factory (method) to instantiate a concrete implementation.



I'm explicitly looking to ignore a long if-elsif-else solution, unless someone can convince me to do otherwise.

Suggestions and criticism is always welcome, thanks!

Answer

In this type of situation, I like to use this pattern:

class Processor

  class << self
    def for(name, data)
      processors[name].new(data)
    end

    def processors
      {
        'MoveEvent' => MoveEventProcessor, 
        'DeleteEvent' => DeleteEventProcessor, 
        'CreateEvent' => CreateEventProcessor
      }
    end

  end

  attr_reader :data
  def initialize(data)
    @data = data
  end

  class MoveEventProcessor < Processor
    #... code to handle this event
  end

  class DeleteEventProcessor < Processor
    #... code to handle this event
  end

  class CreateEventProcessor < Processor
    #... code to handle this event
  end

end

p Processor.for 'MoveEvent', {some: :data}