Skip to content

Instantly share code, notes, and snippets.

@andrewhao
Last active December 19, 2016 17:48
Show Gist options
  • Save andrewhao/acdbd8d6222c04e569d825a7679931a2 to your computer and use it in GitHub Desktop.
Save andrewhao/acdbd8d6222c04e569d825a7679931a2 to your computer and use it in GitHub Desktop.
Evented Rails
module Ridesharing
  class RidesController < ApplicationController
    def post
      # Hail a time-traveling Delorean: 
      HailDelorean.hail(current_user.id)
      render text: 'Hailing a cab, please wait for a response...'
    end
  end
end
module Ridesharing
  class HailDelorean
    include Wisper::Broadcaster
 
    def self.hail(passenger_id)
      broadcast(:hail, passenger_id)
    end
  end
end
 
module DriverRouting
  # Note that this class is both a subscriber and a publisher 
  class FindDriver
    include Wisper::Publisher
 
    def self.hail(passenger_id)
      # Do slow, complex hairy routefinding/optimization/messaging behind the scenes: 
      driver = find_driver_for(passenger_id)
 
      if driver
        broadcast('driver_found', passenger_id, driver.id)
      else
        broadcast('driver_not_found', passenger_id)
      end
    end
  end
end
# Finally, we add handlers (subscribers) to these domain objects:
module Ridesharing
  class NotifyPassengerWithDriverStatus
    def self.driver_found
      # send them a text message :) 
    end
 
    def self.driver_not_found
      # send them a text message :( 
    end
  end
end
# Now let's link it together with subscriptions:
# config/initializers/domain_event_subscriptions.rb 
Ridesharing::HailDelorean.subscribe(DriverRouting::FindDriver, async: true)
DriverRouting::FindDriver.subscribe(Ridesharing::NotifyPassengerWithDriverStatus, async: true)
Wisper.subscribe(AnalyticsListener, scope: "Ridesharing::NotifyPassengerWithDriverStatus", "DriverRouting::FindDriver"], async: true)
module Ridesharing
  class HailDelorean
    include Wisper::Publisher
 
    def hail!(user)
      # broadcast() is a Wisper method to fire an event 
      driver = find_driver(user)
      if driver
        broadcast('hailed', driver)
      else
        broadcast('could_not_hail')
      end
 
    def find_driver(user)
      # Here lies slow, complex domain logic 
      DriverRouting::FindDriver.new(user)
    end
  end
end

Model app behavior from the old way to the new way, with bounded contexts:

Old way:

class TripsController < ApplicationController
module Ridesharing
  class RidesController < ApplicationController
    def post
      # Hail a time-traveling Delorean: 
      command = HailDelorean.new
      command.on('hailed') { |driver|
        render text: "Hailed you a cab: #{driver} is arriving!"
      }
      .on('could_not_hail') { 
        render text: "Sorry, no dice."
      }
      command.hail!(current_user)
    end
  end
end
module Ridesharing
  class RidesController < ApplicationController
    def post
      # snip 
      command = HailDelorean.new(current_user)
 
      # register the subscriber to the triggering action 
      command.subscribe(TrackSegmentAnalytics)
      # snip 
    end
  end
end
class TrackSegmentAnalytics
  def self.hailed(driver)
    # fire analytics event to Segment 
  end
 
  def self.could_not_hail
    # fire analytics event to Segment 
  end
end
# config/initializers/domain_event_subscriptions.rb 
Wisper.subscribe(TrackSegmentAnalytics, scope: "HailDelorean")
 
# alternate form: 
HailDelorean.subscribe(TrackSegmentAnalytics)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment