Skip to content

Instantly share code, notes, and snippets.

@jgaskins
Created November 1, 2015 16:26
Show Gist options
  • Save jgaskins/0011c518359642833bb0 to your computer and use it in GitHub Desktop.
Save jgaskins/0011c518359642833bb0 to your computer and use it in GitHub Desktop.
GrandCentral deep nesting w/ cache invalidation
require 'clearwater/component'
require 'clearwater/cached_render'
module Dispatcher
class Driver
include Clearwater::Component
include Clearwater::CachedRender
attr_reader :driver, :orders
# I'm passing in the driver and orders as "props", but they could easily be retrieved
# from the global store.
def initialize(driver, orders)
@driver = driver
@order = orders
end
# Example constructor for pulling from global state
def initialize(market, driver_id, store=Store)
@driver = store.state[:drivers][market.id][driver_id]
@orders = store.state[:orders][market][:by_driver_id][driver_id]
end
def render
# ...
end
def should_render? previous
!(
drivers.equal?(previous.drivers) &&
orders.equal?(previous.orders)
)
end
end
end
markets = [
Market.new(
id: 1,
name: 'My Example Market',
)
]
drivers = `gon.drivers`.map { |d| Driver.new(Hash.new(d)) }
orders = `gon.orders`.map { |o| Order.new(Hash.new(o)) }
index_drivers_by_market_id_and_driver_id = proc do |drivers|
drivers.group_by(&:market_id).reduce({}) do |hash, (market_id, drivers)|
hash.merge!(
market_id => drivers.reduce({}) { |driver_hash, driver|
driver_hash.merge! driver.id => driver
}
)
end
end
index_orders_by_market_id_etc = proc do |orders|
orders.reduce({}) { |hash, order|
market = markets.find { |m| m.id == order.market_id }
hash[market] ||= {
{
by_id: {},
by_driver_id: {},
}
}
hash[market].merge!(
by_id: hash[market][:by_id].merge(order.id => order),
by_driver_id: hash[market][:by_driver_id] + [order]
)
}
end
initial_state = {
drivers: index_drivers_by_market_id_and_driver_id(drivers),
orders: index_orders_by_market_id_etc(orders),
}
Store = GrandCentral::Store.new(initial_state) do |state, action|
case action
# UpdateOrder is dispatched when we receive an update via WebSocket. We receive a whole new version
# of the order instead of a delta between the two in case we missed previous updates because of
# network disruption), so we can simply replace the old order with the new one.
#
# action.order = A new version of the order constructed from the data from the WebSocket
when Actions::UpdateOrder
order = action.order
market = markets.find { |m| m.id == order.market_id }
# Only update if this action is for a market we're tracking
if market
orders = state[:orders].fetch(market) {
{
by_id: {},
by_driver_id: {},
}
}
state.merge(
orders: state[:orders].merge(
# Orders compartmentalized by market
market => {
# Index orders by id for O(1) selection
by_id: orders[:by_id].merge(order.id => order),
# Another index by driver_id, but drivers can have many orders, so we
# iterate over the array of them, replacing the old with the new.
by_driver_id: orders[:by_driver_id].fetch(order.driver_id) { [] }.map { |o|
if o.id == order.id
order
else
o
end
}
}
),
)
else
state
end
# Return the original state if we didn't handle the action.
else
state
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment